Bash:将“find”输出传输到“readarray”中

Bash:将“find”输出传输到“readarray”中

我正在尝试使用 搜索文件find,并将这些文件放入 Bash 数组中,以便我可以对它们(例如lsgrep它们)执行其他操作。但我不明白为什么readarray不读取find通过管道输入的输出。

假设我当前目录中有两个文件,file1.txt并且file2.txt.所以find输出如下:

$ find . -name "file*"
./file1.txt
./file2.txt

所以我想将其通过管道传输到一个数组中,该数组的两个元素是字符串"./file1.txt""./file2.txt"(显然不带引号)。

我已经尝试过这个,还有其他一些事情:

$ declare -a FILES
$ find . -name "file*" | readarray FILES
$ echo "${FILES[@]}"; echo "${#FILES[@]}"

0

正如您从输出中看到的echo,我的数组是空的。

那么我到底做错了什么?为什么readarray不读取find的输出作为其标准输入并将这些字符串放入数组中?

答案1

使用管道时,bash 在子 shell 中运行命令。因此,该数组已填充,但位于子 shell 中,因此父 shell 无法访问它。您可能还需要该-t选项,以便不将该行分隔符存储在数组成员中,因为它们不是文件名的一部分。

使用进程替换:

readarray -t FILES < <(find .)

请注意,它不适用于路径中包含换行符的文件。除非您能保证不会出现这种情况,否则您需要使用 NUL 分隔记录而不是换行符分隔记录:

readarray -td '' < <(find . -print0)

(该-d选项是在 bash 4.4 中添加的)


¹ 使用该选项时,最后一个管道组件除外lastpipe,但这仅适用于bash.

答案2

正确的解决方案是:

unset a; declare -a a
while IFS= read -r -u3 -d $'\0' file; do
    a+=( "$file" )        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

这类似于什么格雷格的 Bash常见问题解答 020详细解释和这个答案涵盖了

对于奇怪的命名文件(名称中不包含 NUL)、带有空格或换行符没有问题。结果被设置在一个数组中,这对于进一步处理很有用。

答案3

readarray也可以从标准输入读取

readarray FILES <<< "$(find . -name "file*")"; echo "${#FILES[@]}"

答案4

使用shopt -s lastpipe原始问题中显示的解决方案有效,因为最后一个之后的部分|与大多数脚本在同一进程中运行:

shopt -s lastpipe

# This is just the code form the original question.
# Of course it could be done with -print0, etc.
declare -a FILES
find . -name "name*" | readarray -t FILES
echo find and readarray exit status: "${PIPESTATUS[@]}"
echo "${FILES[@]}"
echo "${#FILES[@]}"

然而,只有当lastpipe可以成为项目范围的标准时,这才是实用的(也许您已经有了这样的通用设置set -u,等等)。还要知道它lastpipe仅适用于非交互式 shell。

另一种典型的管道替换< <(...)(进程替换)存在的问题是很难检查子进程是否成功。

相关内容