我正在尝试使用 搜索文件find
,并将这些文件放入 Bash 数组中,以便我可以对它们(例如ls
或grep
它们)执行其他操作。但我不明白为什么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。
另一种典型的管道替换< <(...)
(进程替换)存在的问题是很难检查子进程是否成功。