将命令输出读入Bash中的数组 [英] Reading output of a command into an array in Bash

查看:227
本文介绍了将命令输出读入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:

  1. 在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中的loop方法更好.

  • 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语句在同一行:这只会为read语句设置环境变量IFS .它根本不会影响脚本的其余部分.此变量的目的是告诉read以EOL字符\n中断流.
    • -r:这很重要.它告诉read 不要将反斜杠解释为转义序列.
    • -d '':请注意-d选项与其参数''之间的空格.如果您在此处不留空格,将不会显示'',因为当Bash解析该语句时,它会在 quote remove 步骤中消失.这告诉read在nil字节处停止读取.有些人将其写为-d $'\0',但这并不是必须的. -d ''更好.
    • -a my_array告诉read在读取流时填充数组my_array.
    • 您必须在之后 my_command使用printf '\0'语句,以便read返回0;如果不这样做,实际上并不重要(您只会得到一个返回代码1,如果您不使用set -e也是可以的–仍然不应该这样),但是您只需承担头脑.它更干净,在语义上更正确.请注意,这与printf ''不同,后者不输出任何内容. printf '\0'打印一个空字节,read需要用它来快乐地停止在此处读取(还记得-d ''选项吗?).
    • IFS=$'\n' on the same line as the read statement: this will only set the environment variable IFS for the read statement only. So it won't affect the rest of your script at all. The purpose of this variable is to tell read to break the stream at the EOL character \n.
    • -r: this is important. It tells read 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 tells read 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 tells read to populate the array my_array while reading the stream.
    • You must use the printf '\0' statement after my_command, so that read returns 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 read 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提供了一种在读取的每一行上都具有一个回调的方法,实际上,您甚至可以告诉它仅在读取的 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 [three four]匹配……此时,有些人建议使用set -f禁用滚球:但是看一下:您必须更改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]")'
    

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

    here we're working with the shell!

    这篇关于将命令输出读入Bash中的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆