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


9

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

我当前的代码通过修改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 \"""\").

有人可以帮我吗?谢谢!


choroba的答案说可以做< <(some command returning multiple lines),但这不是您在做什么。
Mikel 2012年

Answers:


13

怎么样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"

在中,使用流程替换是一个明智的解决方案bash,但是,值得注意的是,在subshel​​l范围内定义变量仅是一个问题,在bash中执行相同的操作zsh根本不会出现此问题。
Caleb 2012年

ksh也没有这个问题。
Didi Kohen 2012年

谢谢大家,我尝试了此解决方案,但无法正常运行,请查看我更新的原始帖子,谢谢!
stefan.at.wpf 2012年

@ stefan.at.wpf:您不能随意放置空格和美元符号。它们具有意义。
choroba 2012年

3

IFS仅设置为换行符是不够的。(顺便说一下,为什么还要拆分退格字符?)

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

您可以使用来关闭Globing set -f。在大多数情况下,您设置IFS为使用外壳程序的字段拆分功能时,还需要关闭globbing。使用重新设置set +f

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

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

请注意,大多数外壳程序在单独的子外壳程序中运行管道的每个命令。因此,如果需要设置变量并在循环后使用它们,请将这些命令与循环包装在一起。(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."
}

1

您在更新问题中混合了here-document和here-string语法。

要么使用here-document:

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}")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.