Warm tip: This article is reproduced from serverfault.com, please click

其他-如何将“查找”命令结果存储为Bash中的数组

(其他 - How can I store the "find" command results as an array in Bash)

发布于 2014-04-29 06:07:10

我想将结果保存find为数组。这是我的代码:

#!/bin/bash

echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=`find . -name ${input}`

len=${#array[*]}
echo "found : ${len}"

i=0

while [ $i -lt $len ]
do
echo ${array[$i]}
let i++
done

我在当前目录下得到2个.txt文件。所以我期望'2'作为的结果${len}但是,它打印1。原因是将所有结果都find作为一个元素。我怎样才能解决这个问题?

PS
我发现了几个解决方案,在计算器上有关类似问题。但是,它们有些不同,因此我无法适用于我的情况。我需要在循环之前将结果存储在变量中。再次感谢。

Questioner
Juneyoung Oh
Viewed
0
John1024 2020-05-05 02:54:37

Linux用户的2020更新:

如果你拥有bash的最新版本(4.4-alpha或更高版本)(如在Linux上一样),则应使用Benjamin W.的answer

如果你使用的是Mac OS(我上次检查过)仍使用bash 3.2,或者使用的是较旧的bash,请继续进行下一节。

回答bash 4.3或更早版本

这是将输出结果find放入bash数组的一种解决方案

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

这很棘手,因为通常文件名可以包含空格,换行符和其他对脚本不利的字符。使用find并使文件名安全地分开的唯一方法是使用,该命令-print0将打印以空字符分隔的文件名。如果bash的readarray/mapfile函数支持以空分隔的字符串,但不支持,则不会带来太大的不便Bash的做法read使我们进入了上面的循环。

[此答案最初写于2014年。如果你使用的是最新版本的bash,请参见下面的更新。]

这个怎么运作

  1. 第一行创建一个空数组: array=()

  2. 每次read执行语句时,都会从标准输入中读取以空分隔的文件名。-r选项告诉read你保留反斜杠字符。-d $'\0'告诉read输入将以空分隔。由于我们省略了名称read,因此外壳程序将输入内容放入默认名称:中REPLY

  3. array+=("$REPLY")语句将新文件名附加到数组array

  4. 最后一行结合了重定向和命令替换,以将输出提供findwhile循环的标准输入

为什么要使用流程替代?

如果我们不使用流程替换,则循环可以写成:

array=()
find . -name "${input}" -print0 >tmpfile
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

在上面,的输出find存储在一个临时文件中,该文件用作while循环的标准输入。进程替换的想法是使这些临时文件变得不必要。因此,与其让while循环从其获取标准输入tmpfile不如循环从其获取标准输入<(find . -name ${input} -print0)

流程替换非常有用。在许多要从文件中读取命令的地方,你可以指定进程替换<(...),而不是文件名。有一个类似的形式,>(...)可以代替命令要写入文件的文件名。

像数组一样,进程替换是bash和其他高级Shell的功能。它不是POSIX标准的一部分。

替代方案:lastpipe

如果需要,lastpipe可以使用它代替进程替换(提示:Caesar):

set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS=  read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array

shopt -s lastpipe告诉bash在当前shell(而不是后台)中的管道中运行最后一个命令。这样,array流水线完成后便仍然存在。因为lastpipe仅在关闭作业控制后才会生效,所以我们运行set +m(在脚本中,相对于命令行,默认情况下,作业控制处于关闭状态。)

补充笔记

以下命令创建一个shell变量,而不是一个shell数组:

array=`find . -name "${input}"`

如果要创建一个数组,则需要将括号放在find的输出周围。因此,天真的,一个人可以:

array=(`find . -name "${input}"`)  # don't do this

问题在于外壳程序对的结果执行单词拆分,find因此不能保证数组的元素是你想要的。

更新2019

从4.4-alpha版本开始,bash现在支持一个-d选项,因此不再需要上述循环。相反,可以使用:

mapfile -d $'\0' array < <(find . -name "${input}" -print0)

有关此的更多信息,请参阅(并赞扬)Benjamin W.的答案