将命令的输出读入 Bash 中的数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11426529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:24:30  来源:igfitidea点击:

Reading output of a command into an array in Bash

arraysbash

提问by barp

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

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.

我需要将命令输出中的值读取到一个数组中,然后如果数组的大小小于三个,我将做一些工作。

回答by gniourf_gniourf

The other answers will break if output of command contains spaces (which is rather frequent) or glob characters like *, ?, [...].

如果命令的输出包含空格(相当频繁)或像*, ?, 之类的 glob 字符,则其他答案将中断[...]

To get the output of a command in an array, with one line per element, there are essentially 3 ways:

要在数组中获取命令的输出,每个元素一行,基本上有 3 种方法:

  1. With Bash≥4 use mapfile—it's the most efficient:

    mapfile -t my_array < <( my_command )
    
  2. Otherwise, a loop reading the output (slower, but safe):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
    
  3. 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 '
    mapfile -t my_array < <( my_command )
    
    ' )

    Please make sure you use exactly this form, i.e., make sure you have the following:

    • IFS=$'\n'on the same line as the readstatement:this will only set the environment variable IFSfor the readstatement only.So it won't affect the rest of your script at all. The purpose of this variable is to tell readto break the stream at the EOL character \n.
    • -r: this is important. It tells readto not interpret the backslashes as escape sequences.
    • -d '': please note the space between the -doption and its argument ''. If you don't leave a space here, the ''will never be seen, as it will disappear in the quote removalstep when Bash parses the statement. This tells readto stop reading at the nil byte. Some people write it as -d $'\0', but it is not really necessary. -d ''is better.
    • -a my_arraytells readto populate the array my_arraywhile reading the stream.
    • You must use the printf '\0'statement aftermy_command, so that readreturns 0; it's actually not a big deal if you don't (you'll just get an return code 1, which is okay if you don't use set -e– which you shouldn't anyway), but just bear that in mind. It's cleaner and more semantically correct. Note that this is different from printf '', which doesn't output anything. printf '\0'prints a null byte, needed by readto happily stop reading there (remember the -d ''option?).
  1. Bash≥4 使用mapfile——这是最有效的:

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
    
  2. 否则,循环读取输出(较慢,但安全):

    IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '
    my_array=( $( my_command) )
    
    ' )
  3. 正如查尔斯·达菲 (Charles Duffy) 在评论中所建议的那样(谢谢!),以下可能比第 2 条中的循环方法执行得更好:

    $ # 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'read语句在同一行:这只会IFSread语句设置环境变量所以它根本不会影响脚本的其余部分。此变量的目的是告诉read在 EOL 字符处中断流\n
    • -r: 这个很重要。它告诉read不要将反斜杠解释为转义序列。
    • -d '': 请注意-d选项与其参数之间的空格''。如果您不在此处留空格,''则永远不会看到 ,因为它会在Bash 解析语句时在引用删除步骤中消失。这告诉read在 nil 字节处停止读取。有些人把它写成-d $'\0',但这并不是真正必要的。-d ''更好。
    • -a my_array告诉在读取流时read填充数组my_array
    • 您必须使用afterprintf '\0'语句,以便返回;如果你不使用,这实际上没什么大不了的(你只会得到一个返回码,如果你不使用也没关系——无论如何你都不应该这样做),但请记住这一点。它更干净,在语义上更正确。请注意,这与不输出任何内容的 不同。打印一个空字节,这是愉快地停止阅读所需要的(还记得这个选项吗?)。my_commandread01set -eprintf ''printf '\0'read-d ''


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.

如果可以,即,如果您确定您的代码将在 Bash≥4 上运行,请使用第一种方法。你也可以看到它更短。

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 $linevariable 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).

如果您想使用read,如果您想在读取行时进行一些处理,则循环(方法 2)可能比方法 3 具有优势:您可以直接访问它(通过$line我给出的示例中的变量),并且您还可以访问已读取的行(通过${my_array[@]}我给出的示例中的数组)。

Note that mapfileprovides 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 Nlines read; have a look at help mapfileand the options -Cand -ctherein. (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!).

需要注意的是mapfile提供了一种具有eval'd每行一个回调读取,而事实上,你甚至可以告诉它只是调用这个回调每ň行读取; 看看help mapfile和选项-C-c其中。(我对此的看法是它有点笨拙,但如果您只有简单的事情要做,有时可以使用 - 我真的不明白为什么一开始就实现了这一点!)。



Now I'm going to tell you why the following method:

现在我要告诉你为什么采用以下方法:

$ 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!

is broken when there are spaces:

有空格时被破坏:

$ 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?

Then some people will then recommend using IFS=$'\n'to fix it:

然后有些人会推荐使用IFS=$'\n'来修复它:

$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'

But now let's use another command, with globs:

但是现在让我们使用另一个带有globs 的命令:

my_array=( $(<command>) )

That's because I have a file called tin the current directory… and this filename is matched by the glob[three four]… at this point some people would recommend using set -fto disable globbing: but look at it: you have to change IFSand use set -fto be able to fix a broken technique (and you're not even fixing it really)! when doing that we're really fighting againstthe shell, not working with the shell.

那是因为我t在当前目录中调用了一个文件……并且该文件名与glob匹配[three four]……此时有些人会建议使用set -f来禁用 globbing:但是看看它:您必须更改IFS并使用set -f才能修复损坏的技术(你甚至没有真正修复它)!这样做时,我们实际上是在与shell作斗争,而不是与 shell 一起工作

my_array_length=${#my_array[@]}

here we're working with the shell!

在这里,我们正在使用外壳!

回答by Michael Schlottke-Lakemper

You can use

您可以使用

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length

to store the output of command <command>into the array my_array.

将命令的输出存储<command>到数组中my_array

You can access the length of that array using

您可以使用访问该数组的长度

for element in "${my_array[@]}"
do
   echo "${element}"
done

Now the length is stored in my_array_length.

现在长度存储在my_array_length.

回答by Youness

Imagine that you are going to put the files and directory names (under the current folder) to an array and count its items. The script would be like;

想象一下,您要将文件和目录名称(在当前文件夹下)放入一个数组并计算其项目数。脚本应该是这样的;

##代码##

Or, you can iterate over this array by adding the following script:

或者,您可以通过添加以下脚本来遍历此数组:

##代码##

Please note that this is the core concept and the input is considered to be sanitized before, i.e. removing extra characters, handling empty Strings, and etc. (which is out of the topic of this thread).

请注意,这是核心概念,并且输入之前被认为是经过清理的,即删除多余的字符、处理空字符串等(这超出了本线程的主题)。