将命令的输出读入 Bash 中的数组 [英] Reading output of a command into an array in Bash
问题描述
我需要将脚本中命令的输出读入数组.命令是,例如:
I need to read the output of a command in my script into an array. The command is, for example:
ps aux | grep | grep | x
它像这样一行一行地给出输出:
and it gives the output line by line like this:
10
20
30
我需要把命令输出中的值读到一个数组中,然后如果数组的大小小于3,我会做一些工作.
I need to read the values from the command output into an array, and then I will do some work if the size of the array is less than three.
推荐答案
如果命令的输出包含空格(相当频繁)或诸如 *
、 之类的 glob 字符,则其他答案将中断?
, [...]
.
The other answers will break if output of command contains spaces (which is rather frequent) or glob characters like *
, ?
, [...]
.
要在数组中获取命令的输出,每个元素一行,基本上有 3 种方法:
To get the output of a command in an array, with one line per element, there are essentially 3 ways:
在 Bash≥4 的情况下使用
mapfile
——这是最有效的:
mapfile -t my_array < <( my_command )
否则,读取输出的循环(较慢,但安全):
Otherwise, a loop reading the output (slower, but safe):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
正如查尔斯·达菲 (Charles Duffy) 在评论中所建议的那样(谢谢!),以下可能比第 2 条中的循环方法执行得更好:
As suggested by Charles Duffy in the comments (thanks!), the following might perform better than the loop method in number 2:
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
请确保您完全使用此表格,即确保您拥有以下内容:
Please make sure you use exactly this form, i.e., make sure you have the following:
IFS=$'\n'
与read
语句在同一行:这只会设置环境变量IFS
仅用于read
语句. 所以它根本不会影响脚本的其余部分.此变量的目的是告诉read
在 EOL 字符\n
处中断流.-r
:这很重要.它告诉read
不要将反斜杠解释为转义序列.-d ''
:请注意-d
选项与其参数''
之间的空格.如果这里不留空格,则永远不会看到''
,因为它会在 Bash 解析语句的 引用删除 步骤中消失.这告诉read
在 nil 字节处停止读取.有些人把它写成-d $'\0'
,但这并不是真的必要.-d ''
更好.-a my_array
告诉read
在读取流时填充数组my_array
.- 您必须使用
printf '\0'
语句aftermy_command
,以便read
返回0
;如果你不这样做实际上没什么大不了的(你只会得到一个返回码1
,如果你不使用set -e
无论如何你不应该),但请记住这一点.它更干净,在语义上更正确.请注意,这与printf ''
不同,后者不输出任何内容.printf '\0'
打印一个空字节,read
需要它来愉快地停止阅读(还记得-d ''
选项吗?).
IFS=$'\n'
on the same line as theread
statement: this will only set the environment variableIFS
for theread
statement only. So it won't affect the rest of your script at all. The purpose of this variable is to tellread
to break the stream at the EOL character\n
.-r
: this is important. It tellsread
to not interpret the backslashes as escape sequences.-d ''
: please note the space between the-d
option and its argument''
. If you don't leave a space here, the''
will never be seen, as it will disappear in the quote removal step when Bash parses the statement. This tellsread
to stop reading at the nil byte. Some people write it as-d $'\0'
, but it is not really necessary.-d ''
is better.-a my_array
tellsread
to populate the arraymy_array
while reading the stream.- You must use the
printf '\0'
statement aftermy_command
, so thatread
returns0
; it's actually not a big deal if you don't (you'll just get an return code1
, which is okay if you don't useset -e
– which you shouldn't anyway), but just bear that in mind. It's cleaner and more semantically correct. Note that this is different fromprintf ''
, which doesn't output anything.printf '\0'
prints a null byte, needed byread
to happily stop reading there (remember the-d ''
option?).
<小时>
如果可以,即如果您确定您的代码将在 Bash≥4 上运行,请使用第一种方法.你也可以看到它更短了.
If you can, i.e., if you're sure your code will run on Bash≥4, use the first method. And you can see it's shorter too.
如果您想使用 read
,如果您想在读取行时进行一些处理,则循环(方法 2)可能比方法 3 有优势:您可以直接访问它(通过我给出的示例中的 $line
变量),您还可以访问已读取的行(通过示例中的数组 ${my_array[@]}
我给了).
If you want to use read
, the loop (method 2) might have an advantage over method 3 if you want to do some processing as the lines are read: you have direct access to it (via the $line
variable in the example I gave), and you also have access to the lines already read (via the array ${my_array[@]}
in the example I gave).
请注意,mapfile
提供了一种方法,可以在读取的每一行上进行 eval 回调,实际上您甚至可以告诉它每 N 只调用此回调读取的行;查看 help mapfile
以及其中的选项 -C
和 -c
.(我对此的看法是它有点笨拙,但如果您只有简单的事情要做,有时可以使用它 - 我真的不明白为什么一开始就实现了!).
Note that mapfile
provides a way to have a callback eval'd on each line read, and in fact you can even tell it to only call this callback every N lines read; have a look at help mapfile
and the options -C
and -c
therein. (My opinion about this is that it's a little bit clunky, but can be used sometimes if you only have simple things to do — I don't really understand why this was even implemented in the first place!).
现在我要告诉你为什么采用以下方法:
Now I'm going to tell you why the following method:
my_array=( $( my_command) )
在有空格时被破坏:
$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!
然后有些人会推荐使用 IFS=$'\n'
来修复它:
Then some people will then recommend using IFS=$'\n'
to fix it:
$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!
但是现在让我们使用另一个命令,使用 globs:
But now let's use another command, with globs:
$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?
那是因为我在当前目录中有一个名为 t
的文件……而这个文件名与 glob [三四]
相匹配……在这一点上,有些人会建议使用 set -f
来禁用 globbing:但是看看它:您必须更改 IFS
并使用 set -f
> 能够修复损坏的技术(而您甚至没有真正修复它)!这样做时,我们实际上是对抗外壳,而不是使用外壳.
That's because I have a file called t
in the current directory… and this filename is matched by the glob [three four]
… at this point some people would recommend using set -f
to disable globbing: but look at it: you have to change IFS
and use set -f
to be able to fix a broken technique (and you're not even fixing it really)! when doing that we're really fighting against the shell, not working with the shell.
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
这里我们正在使用 shell!
here we're working with the shell!
这篇关于将命令的输出读入 Bash 中的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!