我有一个返回多行的命令。为了进一步处理,我需要处理这些行中的每一行。
我当前的代码通过修改 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
那么read
在while
循环中呢?
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