问题描述
我正在尝试将 find
的结果保存为数组.这是我的代码:
I am trying to save the result from find
as arrays.Here is my code:
#!/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
的所有结果作为一个元素.我该如何解决这个问题?
I get 2 .txt files under current directory.So I expect '2' as result of ${len}
. However, it prints 1.The reason is that it takes all result of find
as one elements.How can I fix this?
附言
我在 StackOverFlow 上找到了几个关于类似问题的解决方案.但是,它们有点不同,所以我不能申请我的情况.我需要在循环之前将结果存储在一个变量中.再次感谢.
P.S
I found several solutions on StackOverFlow about a similar problem. However, they are a little bit different so I can't apply in my case. I need to store the results in a variable before the loop. Thanks again.
推荐答案
2020 年 Linux 用户更新:
如果您拥有最新版本的 bash(4.4-alpha 或更高版本),就像您在 Linux 上可能那样,那么您应该使用 Benjamin W. 的回答.
如果您使用的是 Mac OS(我上次检查过)仍然使用 bash 3.2,或者使用的是较旧的 bash,请继续阅读下一节.
If you are on Mac OS, which —last I checked— still used bash 3.2, or are otherwise using an older bash, then continue on to the next section.
这是将 find
的输出放入 bash
数组的一种解决方案:
Here is one solution for getting the output of find
into a bash
array:
array=()
while IFS= read -r -d $' '; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
这很棘手,因为通常文件名可以包含空格、换行符和其他脚本敌对字符.使用 find
并使文件名彼此安全分隔的唯一方法是使用 -print0
打印以空字符分隔的文件名.如果 bash 的 readarray
/mapfile
函数支持空分隔字符串但它们不支持,这不会带来太大的不便.Bash 的 read
确实如此,这将我们引向上面的循环.
This is tricky because, in general, file names can have spaces, new lines, and other script-hostile characters. The only way to use find
and have the file names safely separated from each other is to use -print0
which prints the file names separated with a null character. This would not be much of an inconvenience if bash's readarray
/mapfile
functions supported null-separated strings but they don't. Bash's read
does and that leads us to the loop above.
[此答案最初写于 2014 年.如果您有最新版本的 bash,请查看下面的更新.]
第一行创建一个空数组:
array=()
每次执行 read
语句时,都会从标准输入中读取一个以空值分隔的文件名.-r
选项告诉 read
保留反斜杠字符.-d $' '
告诉 read
输入将以空值分隔.由于我们省略了 read
的名称,因此 shell 将输入放入默认名称:REPLY
.
Every time that the read
statement is executed, a null-separated file name is read from standard input. The -r
option tells read
to leave backslash characters alone. The -d $' '
tells read
that the input will be null-separated. Since we omit the name to read
, the shell puts the input into the default name: REPLY
.
array+=("$REPLY")
语句将新文件名附加到数组array
.
The array+=("$REPLY")
statement appends the new file name to the array array
.
最后一行结合了重定向和命令替换,将 find
的输出提供给 while
循环的标准输入.
The final line combines redirection and command substitution to provide the output of find
to the standard input of the while
loop.
为什么要使用进程替换?
如果我们不使用进程替换,循环可以写成:
Why use process substitution?
If we didn't use process substitution, the loop could be written as:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $' '; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
在上面,find
的输出存储在一个临时文件中,该文件用作while 循环的标准输入.进程替换的想法是使此类临时文件变得不必要.所以,我们可以让它从 <(find . -name ${input}-print0)
.
In the above the output of find
is stored in a temporary file and that file is used as standard input to the while loop. The idea of process substitution is to make such temporary files unnecessary. So, instead of having the while
loop get its stdin from tmpfile
, we can have it get its stdin from <(find . -name ${input} -print0)
.
进程替换非常有用.在命令想要从文件读取的许多地方,您可以指定进程替换,<(...)
,而不是文件名.有一个类似的形式,>(...)
,可以用来代替命令要写入到文件的文件名.
Process substitution is widely useful. In many places where a command wants to read from a file, you can specify process substitution, <(...)
, instead of a file name. There is an analogous form, >(...)
, that can be used in place of a file name where the command wants to write to the file.
与数组一样,进程替换是 bash 和其他高级 shell 的一项功能.它不是 POSIX 标准的一部分.
Like arrays, process substitution is a feature of bash and other advanced shells. It is not part of the POSIX standard.
如果需要,可以使用 lastpipe
代替进程替换(提示:Caesar):
If desired, lastpipe
can be used instead of process substitution (hat tip: Caesar):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $' '; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
告诉 bash 在当前 shell(而不是后台)中运行管道中的最后一个命令.这样,array
在管道完成后仍然存在.因为lastpipe
只有在job control关闭的情况下才会生效,所以我们运行set +m
.(在脚本中,与命令行相反,作业控制默认关闭.)
shopt -s lastpipe
tells bash to run the last command in the pipeline in the current shell (not the background). This way, the array
remains in existence after the pipeline completes. Because lastpipe
only takes effect if job control is turned off, we run set +m
. (In a script, as opposed to the command line, job control is off by default.)
以下命令创建一个 shell 变量,而不是一个 shell 数组:
The following command creates a shell variable, not a shell array:
array=`find . -name "${input}"`
如果你想创建一个数组,你需要在 find 的输出周围加上括号.所以,天真地,一个人可以:
If you wanted to create an array, you would need to put parens around the output of find. So, naively, one could:
array=(`find . -name "${input}"`) # don't do this
问题是shell对find
的结果进行了分词,所以不能保证数组的元素就是你想要的.
The problem is that the shell performs word splitting on the results of find
so that the elements of the array are not guaranteed to be what you want.
从 4.4-alpha 版本开始,bash 现在支持 -d
选项,因此不再需要上述循环.相反,可以使用:
Starting with version 4.4-alpha, bash now supports a -d
option so that the above loop is no longer necessary. Instead, one can use:
mapfile -d $' ' array < <(find . -name "${input}" -print0)
有关这方面的更多信息,请参阅(并点赞)Benjamin W. 的回答.
For more information on this, please see (and upvote) Benjamin W.'s answer.
这篇关于如何存储“查找"?命令结果作为 Bash 中的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!