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

bar*_*arp 83 arrays bash

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

ps aux | grep | grep | x 
Run Code Online (Sandbox Code Playgroud)

并且它按行给出输出,如下所示:

10
20
30
Run Code Online (Sandbox Code Playgroud)

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

gni*_*urf 116

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

要在数组中获取命令的输出,基本上有两种方法:

  1. 使用Bash≥4使用-it mapfile是最有效的:

    mapfile -t my_array < <( my_command )
    
    Run Code Online (Sandbox Code Playgroud)
  2. 否则,循环读取输出(较慢但安全):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
    
    Run Code Online (Sandbox Code Playgroud)

你可能会看到很多这样的东西:

my_array=( $( my_command) )
Run Code Online (Sandbox Code Playgroud)

但是不要用它!看看它是如何破碎的:

$ # this is the command used to test:
$ echo "one two"; echo "three four"
one two
three four
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # Not good! now look:
$ 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!
Run Code Online (Sandbox Code Playgroud)

然后有人会建议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!
Run Code Online (Sandbox Code Playgroud)

但现在让我们使用另一个命令:

$ 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?
Run Code Online (Sandbox Code Playgroud)

那是因为我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]")'
Run Code Online (Sandbox Code Playgroud)

在这里我们正在使用shell!

  • 顺便说一下,在shell脚本中使用这个语法`<<(command)`,shebang行应该是`#!/ bin/bash` - 如果以`#!/ bin/sh`运行,bash将退出并出现语法错误. (4认同)
  • 太好了,我之前从未听说过`mapfile`,这正是我多年来一直缺少的东西。我想最新的`bash`版本有很多不错的新功能,我应该花几天时间阅读文档并写下一份不错的备忘单。 (3认同)
  • @Vito:确实,这个答案仅适用于 Bash,但这应该不是问题,因为严格兼容的 POSIX shell 甚至不实现数组(`sh` 和 `dash` 根本不知道数组,当然,除了位置参数`$@` 数组)。 (2认同)
  • 作为不需要bash 4.0的另一种选择,请考虑`IFS = $'\ n'read -r -d''-a my_array &lt;&lt;(my_command &amp;&amp; printf'\ 0')`-两者都可以在bash中正常工作3.x,并且还将失败的退出状态从“ my_command”传递到“ read”。 (2认同)
  • 无论出于何种原因,从终端运行此命令都可以使用“source”,但从 cron 作业中,仅使用“bash”调用才有效。当我从 cron 作业“source”时,它显示:意外标记“&lt;”附近出现语法错误。知道为什么吗? (2认同)

Mic*_*per 77

您可以使用

my_array=( $(<command>) )
Run Code Online (Sandbox Code Playgroud)

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

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

my_array_length=${#my_array[@]}
Run Code Online (Sandbox Code Playgroud)

现在长度存储在my_array_length.

  • 如果$(命令)的输出有空格和多行空格怎么办?我添加了"$(command)",它将所有行的所有输出放入数组的第一个[0]元素中. (14认同)
  • @ ikwyl6解决方法是将命令输出分配给变量,然后使用它创建数组或将其添加到数组中.`VAR ="$(<command>)"`然后`my_array =("$ VAR")`或`my_array + =("$ VAR")` (3认同)
  • 如果&lt;command&gt;中的行包含空格,则不起作用。 (3认同)
  • @ikwyl6如果输入包含换行符,则按照其他答案的建议进行操作并更改“IFS”以反映这一点。`IFS=$'\n' my_array=( $(&lt;命令&gt;) )`。这对我有用 (3认同)

You*_*ess 9

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

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length
Run Code Online (Sandbox Code Playgroud)

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

for element in "${my_array[@]}"
do
   echo "${element}"
done
Run Code Online (Sandbox Code Playgroud)

  • 由于上述答案中提到的原因,这个想法很糟糕 (4认同)
  • @HubertGrzeskowiak “上面”和“下面”在 SO 中没有任何意义。你指的是谁的答案? (2认同)
  • 好点@Lou。我的意思是/sf/answers/2305198241/ (2认同)