我想将结果保存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
我发现了几个解决方案,在计算器上有关类似问题。但是,它们有些不同,因此我无法适用于我的情况。我需要在循环之前将结果存储在变量中。再次感谢。
如果你拥有bash的最新版本(4.4-alpha或更高版本)(如在Linux上一样),则应使用Benjamin W.的answer。
如果你使用的是Mac OS(我上次检查过)仍使用bash 3.2,或者使用的是较旧的bash,请继续进行下一节。
这是将输出结果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,请参见下面的更新。]
第一行创建一个空数组: array=()
每次read
执行该语句时,都会从标准输入中读取以空分隔的文件名。该-r
选项告诉read
你保留反斜杠字符。该-d $'\0'
告诉read
输入将以空分隔。由于我们省略了名称read
,因此外壳程序将输入内容放入默认名称:中REPLY
。
该array+=("$REPLY")
语句将新文件名附加到数组array
。
最后一行结合了重定向和命令替换,以将输出提供find
给while
循环的标准输入。
如果我们不使用流程替换,则循环可以写成:
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
可以使用它代替进程替换(提示: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
因此不能保证数组的元素是你想要的。
从4.4-alpha版本开始,bash现在支持一个-d
选项,因此不再需要上述循环。相反,可以使用:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
有关此的更多信息,请参阅(并赞扬)Benjamin W.的答案。
@JuneyoungOh很高兴提供了帮助。我添加了一部分流程替换。
@Rockallite这是一个很好的观察,但是不完整。虽然确实没有将我们分成多个单词,但仍然需要
IFS=
避免从输入行的开头或结尾删除空格。您可以通过将的输出read var <<<' abc '; echo ">$var<"
与的输出进行比较,轻松地进行测试IFS= read var <<<' abc '; echo ">$var<"
。在前一种情况下,之前和之后的空格abc
将被删除。在后者中则不是。以空格开头或结尾的文件名可能不常见,但是,如果存在,我们希望它们正确处理。嗨,我执行您的代码后,收到意外令牌附近的消息语法错误
<'
<<(找到aaa /-不是-newermt“ $ last_build_timestamp_v” -type f -print0)'注意:
''
可以使用更简单的方法代替$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
@theeagle我以为你打算写
BLAH=$(find . -name '*.php')
。正如答案中所讨论的那样,该方法仅在有限的情况下适用,但通常不适用于所有文件名,并且不会产生OP所期望的array。