如何在 zsh 中正确收集一系列行

Ame*_*ina 54 zsh shell-script

我认为以下内容会将 的输出分组为my_command一组行:

IFS='\n' array_of_lines=$(my_command);
Run Code Online (Sandbox Code Playgroud)

使得$array_of_lines[1]要提到的第一行中的输出my_command$array_of_lines[2]以在第二,等等。

但是,上面的命令似乎效果不佳。它似乎也分割了my_command字符周围的输出n,正如我已经检查过的那样print -l $array_of_lines,我相信它会逐行打印数组的元素。我还检查了这一点:

echo $array_of_lines[1]
echo $array_of_lines[2]
...
Run Code Online (Sandbox Code Playgroud)

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

IFS='\n' array_of_lines=$(eval my_command);
Run Code Online (Sandbox Code Playgroud)

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

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

array_of_lines=("${(@f)$(my_command)}");
Run Code Online (Sandbox Code Playgroud)

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

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

一季度。在行数组中收集命令输出的“正确”方法是什么?

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

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

Gil*_*il' 85

特尔,博士:

array_of_lines=("${(@f)$(my_command)}")
Run Code Online (Sandbox Code Playgroud)

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

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

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

IFS=$'\n' array_of_lines=($(my_command))
Run Code Online (Sandbox Code Playgroud)

您可以通过将 中的空白字符加倍来保留除最后以外的空行IFS

IFS=$'\n\n' array_of_lines=($(my_command))
Run Code Online (Sandbox Code Playgroud)

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

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'
Run Code Online (Sandbox Code Playgroud)

(假设 的输出my_command不以非分隔行结尾;另请注意,您会丢失 的退出状态my_command

请注意,上面的所有代码段都保留IFS了非默认值,因此它们可能会弄乱后续代码。为了保持IFSlocal的设置,把整个东西放到一个你声明IFSlocal的函数中(这里还要注意保留命令的退出状态):

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
Run Code Online (Sandbox Code Playgroud)

但我建议不要惹恼IFS; 相反,使用f扩展标志在换行符上拆分(?Q1):

array_of_lines=("${(@f)$(my_command)}")
Run Code Online (Sandbox Code Playgroud)

或保留尾随空行:

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

的价值在IFS那里无关紧要。我怀疑您在测试中使用了一个拆分命令IFS来打印$array_of_lines(?Q3)。

  • 这太复杂了!`"${(@f)...}"` 与 `${(f)"..."}` 相同,但方式不同。双引号内的`(@)` 表示“每个数组元素产生一个单词”,而`(f)` 表示“通过换行符拆分为一个数组”。PS:请链接到 [the docs](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags) (8认同)
  • @flyingsheep,没有`${(f)"..."}` 会跳过空行,`"${(@f)...}"` 会保留它们。这与 `$argv` 和 `"$argv[@]"` 之间的区别相同。保存数组所有元素的 `"$@"` 东西来自 70 年代后期的 Bourne shell。 (3认同)

ang*_*gus 6

两个问题:首先,显然双引号也不能解释反斜杠转义(对此很抱歉:)。使用$'...'引号。根据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
Run Code Online (Sandbox Code Playgroud)

我无法回答你的 Q3。我希望我永远不必知道这些深奥的事情:)。