如何将一个命令的输出赋值给一个数组?

112

我需要将grep命令的结果赋值给一个数组...例如:

grep -n "search term" file.txt | sed 's/:.*//'

这导致出现了许多带有行号的行,其中包含搜索项。

1
3
12
19

最简单的方法是将它们分配给一个Bash数组。如果只是分配给一个变量,它们会变成以空格分隔的字符串。


请参考这个问题 - beerbajay
在Bash中,空格分隔的字符串很容易遍历。 - Shiplu Mokaddim
哦...应该更彻底地搜索。谢谢。 - ceiling cat
顺便提一下,使用 grep 获取某些内容的行号并最终将它们传递给具有自己的正则表达式支持的工具是一种常见的反模式。然后它可以找到与 grep 找到的相同行,而不必绕过匹配行号的步骤。 - tripleee
这个回答解决了你的问题吗?如何使用从另一个命令中输出的管道初始化bash数组? - zypA13510
3个回答

181
为了将命令的输出赋值给一个数组,你需要在数组赋值语句中使用一个命令替换。对于一个一般的命令command,语法看起来像这样:

arr=( $(command) )
在本例中,OP的意思是:
arr=($(grep -n "search term" file.txt | sed 's/:.*//'))

内部的$()运行命令,而外部的()使输出成为数组。问题在于当命令输出包含空格时,它将无法正常工作。为解决这个问题,您可以将IFS设置为\n

IFS=$'\n' arr=($(grep -n "search term" file.txt | sed 's/:.*//'))

你也可以通过对数组的每个元素进行扩展来避免使用sed:

arr=($(grep -n "search term" file.txt))
arr=("${arr[@]%%:*}")

@bastaPasta,这不是数组赋值。如果您没有使用支持数组的shell,则可能无法使用数组赋值。 - jordanm
@bastaPasta,只要你的“元素”不包含空格,它就可以。还有其他模拟shell数组的方法,可以通过set和特殊数组$@实现。 - jordanm
4
请注意,array=( $(command) ) 被认为是一个反模式,并且是 BashPitfalls #50 的主题。 - Charles Duffy
1
作为一种更好的实践方法,仍然兼容bash 3.x,请考虑使用read -r -d''-a arr < <(grep ... | sed ... && printf'\0'),它避免了对command输出进行全局匹配和字符串分割,并且还通过管道传递非零退出状态(将它们合并为1,因为缺少尾部定界符会使read失败)。 - Charles Duffy
1
@jordanm 感谢您的回复。今天回来看这个问题帮助我找到了根本原因,它与设置IFS或提出的解决方案无关。问题是我的输入中有一个悬挂空格,我在上游的正则表达式中没有考虑到它。(对于任何好奇的人,请注意agvtool在每行输出末尾留下尾随空格) - levigroker
显示剩余5条评论

13

用空格分隔的字符串在bash中很容易遍历。

# save the ouput
output=$(grep -n "search term" file.txt | sed 's/:.*//')

# iterating by for.
for x in $output; do echo $x; done;

# awk
echo $output | awk '{for(i=1;i<=NF;i++) print $i;}'

# convert to an array
ar=($output)
echo ${ar[3]} # echos 4th element

如果你考虑文件名中的空格,可以使用find . -printf "\"%p\"\n"


5
但对于带有空格的文件名存在问题。 - jordanm
@jordanm 使用 find . -printf "\"%p\"\n" - Shiplu Mokaddim
@ShipluMokaddim,那样行不通。使用这样的“find”命令for x in $output将导致一个名为“two words”的文件被迭代为一个值“two”和另一个值“words”。 - Charles Duffy
1
@ShipluMokaddim,如果您使用ar =($output)并以这种方式分配output,则同样的情况也是正确的,因为参数扩展和命令替换结果不会通过引号删除解析阶段。 - Charles Duffy

5

@Charles Duffy在评论中提供了Bash反模式文档,这些文档给出了最正确的答案:

readarray -t arr < <(grep -n "search term" file.txt | sed 's/:.*//')

他的评论:

注意,array=( $(command) ) 被认为是反模式,并且是 BashPitfalls #50 的主题。- Charles Duffy 于2020年11月16日14:07


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接