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


110

我需要将脚本中的命令输出读入数组。该命令例如是:

ps aux | grep | grep | x 

它逐行给出输出,如下所示:

10
20
30

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


5
嘿,@ barp,回答您的问题,以免您的类型浪费整个社区。
詹姆斯

9
@James问题不在于他没有回答他的问题...这是一个Q / A网站。他只是没有他们标记为已回答。他应该标记它们。暗示。@ barp
DDPWNAGE

4
请@barp,将问题标记为已回答。
smonff '16

相关:在Bash中循环浏览文件的内容,因为通过进程替换读取命令的输出类似于从文件读取。
codeforester

Answers:


160

如果命令的输出包含空格其他的答案将打破(这是相当常见的)glob的或类似的字符*?[...]

要获得数组中命令的输出(每个元素一行),本质上有3种方法:

  1. 如果Bash≥4,则使用mapfile效率最高:

    mapfile -t my_array < <( my_command )
  2. 否则,将循环读取输出(速度较慢,但​​很安全):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
  3. 正如查尔斯·达菲(Charles Duffy)在评论中所建议的(谢谢!),以下方法可能比数字2中的loop方法更好。

    IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )

    请确保您完全使用此表格,即,请确保您具有以下内容:

    • IFS=$'\n' read语句在同一行:这只会IFS read语句设置环境变量因此,它完全不会影响脚本的其余部分。此变量的目的是告知read要在EOL字符处中断流\n
    • -r: 这个很重要。它告诉read 您不要将反斜杠解释为转义序列。
    • -d '':请注意-d选项与其参数之间的空格''。如果您在此处不留空格,那么''它将永远不会被看到,因为当Bash解析该语句时,它将在引用删除步骤中消失。这告诉read您在nil字节处停止读取。有人将其写为-d $'\0',但这并不是必须的。-d ''更好。
    • -a my_array告诉readmy_array在读取流时填充数组。
    • 您必须在之后使用该printf '\0'语句,以便返回;如果您不这样做,那么实际上并不重要(您只会得到一个返回码,如果您不使用它就可以了,无论如何都不应该这样做),但是请牢记这一点。它更干净,在语义上更正确。请注意,这与不输出任何内容。打印一个空字节,愉快地停止在该位置读取(记住该选项?)。 my_commandread01set -eprintf ''printf '\0'read-d ''

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

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

注意,这mapfile提供了一种在读取的每一行上都具有一个回调的方法,实际上,您甚至可以告诉它仅每读取N行就调用一次该回调。看看help mapfile和选项-C,并-c在其中。(对此我的看法是,它有点笨拙,但是如果您只需要做简单的事情,有时可以使用它-我真的不明白为什么它首先要实现!)。


现在,我将告诉您为什么使用以下方法:

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'来修复它:

$ 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的命令:

$ 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来禁用globbing:但是请看一下:您必须进行更改IFS并使用set -f才能修复破损的技术(您甚至还没有真正修复它)!这样做时,我们实际上是在与 shell进行斗争,而不是与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]")'

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


4
真是太好了,我以前从未听说mapfile过,这正是我多年来一直缺少的东西。我猜最近的版本bash具有许多不错的新功能,我应该花几天时间阅读文档并写下一份不错的备忘单。
Gene Pavlovsky

6
顺便说一句,要< <(command)在外壳程序脚本中使用此语法,shebang行应为#!/bin/bash-如果以身份运行#!/bin/sh,则bash将退出并显示语法错误。
Gene Pavlovsky

1
扩展@GenePavlovsky的帮助性注释,该脚本还必须使用bash命令bash my_script.sh而不是sh命令运行sh my_script.sh
Vito

2
@Vito:的确,这个答案仅适用于Bash,但这不应该是一个问题,因为严格遵守的POSIX shell甚至都没有实现数组(sh并且dash根本不了解数组,当然,除了位置参数$@数组)。
gniourf_gniourf

3
作为不需要bash 4.0的另一种选择,请考虑IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')-它既可以在bash 3.x中正常工作,又可以通过退出失败状态从my_command传递到read
查尔斯·达菲,

86

您可以使用

my_array=( $(<command>) )

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

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

my_array_length=${#my_array[@]}

现在,长度存储在中my_array_length


19
如果$(command)的输出有空格和多行带空格怎么办?我添加了“ $(command)”,它将所有行的所有输出放入数组的第一个[0]元素。
ikwyl6

3
@ ikwyl6的解决方法是将命令输出分配给变量,然后使用该变量创建数组或将其添加到数组。VAR="$(<command>)"然后my_array=("$VAR")my_array+=("$VAR")
Vito

10

想象一下,您要将文件和目录名称(在当前文件夹下)放置到数组中并计算其项目。脚本就像;

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

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

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

请注意,这是核心概念,输入之前应被清理,例如,删除多余的字符,处理空的String等,(这不在本主题之内)。


3
出于上述答案中提到的原因,这是一个糟糕的主意
Hubert Grzeskowiak
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.