通过换行符分割命令输出?

通过换行符分割命令输出?

我有一个返回多行的命令。为了进一步处理,我需要处理这些行中的每一行。

我当前的代码通过修改 IFS 来工作(内部字段分隔符):

ROWS=$(some command returning multiple lines)

O=$IFS #save original IFS
IFS=$(echo -en "\n\b") # set IFS to linebreak

for ROW in ${ROWS[@]}
do
  echo "$ROW"
done

IFS=$O #restore old IFS

我想知道,是否有更好的方法来访问多行输出的单行,而不修改 IFS?特别是通过修改 IFS,我的脚本的可读性变得很差。

更新:我很难得到答案,例如来自 choroba 的答案:

while IFS= read -r line ; do
    let var+=line #line 42
done << $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
echo "$var" # line 44

给我

./bla.sh: row 44: Warning: here-document at line 43 delimited by end-of-file (wanted `$(sqlite3 -list -nullvalue NULL -separator , /var/log/asterisk/master.db ${QUERY})')
./bla.sh: row 42: let: echo "": syntax error: invalid arithmetic operator. (error causing character is \"""\").

任何人都可以帮我解决这个问题吗?谢谢!

答案1

那么readwhile循环中呢?

some command returning multiple lines | while IFS= read -r line ; do
    echo "$line"
done

但请注意,管道在子 shell 中运行,这意味着您无法更改脚本其余部分的变量值。如果你需要一些东西从 while 循环中生存下来,你可以使用 bash 的进程替换:

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"

答案2

仅设置IFS换行符是不够的。 (顺便问一下,为什么你还要在退格字符处进行分割?)

在您的代码中,${ROWS[@]}(这是一种奇怪的编写方式$ROWS-ROWS不是数组)没有用双引号引起来。 (如果它在双引号内,您将得到一个字符串,因为ROWS它不是一个数组。)因此 shell 将变量的值拆分为每个IFS字符的字段,然后将每个字段视为全局模式。例如,如果命令打印的其中一行包含单个字符*,则该字符将被当前目录中的文件名替换。

您可以使用 关闭通配符set -f。在大多数情况下,当您设置IFS使用 shell 的字段分割功能时,您还需要关闭通配符。使用 将其重新打开set +f

逐行读取命令输出的可靠习惯用法是while IFS= read -r

some command returning multiple lines |
while IFS= read -r ROW; do
done

请注意,大多数 shell 在单独的子 shell 中运行管道的每个命令。因此,如果您需要设置变量并在循环后使用它们,请将这些命令与循环一起包装在一个组中。 (Ksh 和 zsh 是例外,它们在父 shell 中运行管道的最后一个命令。)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}

答案3

您在更新问题中混合了此处文档和此处字符串语法。

要么使用此处文档:

while IFS= read -r line ; do
    let var+=line #line 42
done <<ENDMARK
$(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
ENDMARK

或者这里的字符串:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")

答案4

根据脚本的具体情况,使用简单的管道机制连接命令可能会产生非常可读的脚本。

在这种情况下,您可以使用xargs分割多行并为每行执行一个命令。这只是 的默认行为xargs

(some command returning multiple lines) | xargs -I{} echo {}

或者作为一个工作演示:

$ echo -e "line 1\nline 2" | xargs -I{} echo "next line: {}"

next line: line 1
next line: line 2

相关内容