如何在zsh中正确收集行数组


42

我认为以下内容将以行的形式对输出进行分组my_command

IFS='\n' array_of_lines=$(my_command);

因此,$array_of_lines[1]它将指向输出中的第一行my_command$array_of_lines[2]指向第二行,依此类推。

但是,上面的命令似乎无法正常工作。就像我检查过的一样,它似乎也将my_command字符的输出分割开了,我相信它会逐行打印数组的元素。我也用以下方法对此进行了检查:nprint -l $array_of_lines

echo $array_of_lines[1]
echo $array_of_lines[2]
...

在第二次尝试中,我认为添加eval可能会有所帮助:

IFS='\n' array_of_lines=$(eval my_command);

但我得到的结果与没有它时完全相同。

最后,按照zsh中带空格的List元素的答案,我还尝试了使用参数扩展标志,而不是IFS告诉zsh如何拆分输入并将元素收集到数组中,即:

array_of_lines=("${(@f)$(my_command)}");

但我仍然得到相同的结果(发生n

有了这个,我有以下问题:

Q1。有哪些“适当”的方式来收集多行代码中的命令输出?

Q2。如何指定IFS仅在换行符上分割?

Q3。如果我在上面的第三次尝试中使用参数扩展标志(即使用@f)来指定拆分,则zsh是否会忽略IFS?的值?为什么上面没有用?

Answers:


71

TL,DR:

array_of_lines=("${(@f)$(my_command)}")

第一个错误(→Q2):IFS='\n'设置IFS为两个字符\n。要设置IFS为换行符,请使用IFS=$'\n'

第二个错误:要将变量设置为数组值,则需要在元素周围加上括号:array_of_lines=(foo bar)

这将起作用,除了它会去除空行,因为连续的空格被视为单个分隔符:

IFS=$'\n' array_of_lines=($(my_command))

您可以保留空行,直到最后,通过将以下字符加倍IFS

IFS=$'\n\n' array_of_lines=($(my_command))

为了保持尾随空行,您还必须在命令的输出中添加一些内容,因为这是在命令替换本身中发生的,而不是从解析中发生的。

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(假设的输出my_command未以无界线结尾;还请注意,您的退出状态为my_command

请注意,以上所有代码段均保留IFS其非默认值,因此它们可能会使后续代码混乱。要保留IFS本地设置,请将整个内容放入您声明IFS本地的函数中(此处还要注意保留命令的退出状态):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

但是我建议不要弄乱IFS; 而是使用f扩展标记在换行符上进行分割(→Q1):

array_of_lines=("${(@f)$(my_command)}")

或保留尾随的空行:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

的值在IFS那里无关紧要。我怀疑您使用了一条拆分后IFS可以$array_of_lines在测试中打印的命令(→Q3)。


7
这太复杂了!"${(@f)...}"与相同${(f)"..."},但方式不同。(@)内双引号表示“每个数组元素产生一个单词”,(f)表示“由换行符分割成数组”。PS:请链接到文档
飞羊

3
@ flyingsheep,no ${(f)"..."}不会跳过空行,"${(@f)...}"保留它们。$argv和之间的区别相同"$argv[@]"。这"$@"件事保持一个数组中的所有元素来自于70年代末的Bourne shell。
斯蒂芬·查泽拉斯

4

有两个问题:首先,显然双引号也不能解释反斜杠转义符(很抱歉:)。使用$'...'引号。根据man zshparam,要收集数组中的单词,您需要将其括在括号中。所以这有效:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

我无法回答您的第三季度。我希望我永远不必知道这样深奥的东西:)。


-2

您也可以使用tr将换行符替换为空格:

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done

3
如果行包含空格怎么办?
don_crissti

2
这是没有意义的。SPC和NL均为默认值$IFS。将一个翻译为另一个没有区别。
–StéphaneChazelas

我的编辑合理吗?我无法按原样工作,
John P

(超时)我承认我在没有真正理解其意图的情况下对其进行了编辑,但是我认为翻译是基于字符串操作进行分离的一个很好的开始。我不会翻译成空格,也不会在空格上拆分,除非期望的行为更像echo,其中输入或多或少都是由谁在乎的单词堆。
John P
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.