我正在尝试将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
的所有结果作为一个元素。
我该如何解决这个问题?
P.S
我在StackOverFlow上找到了几个关于类似问题的解决方案。但是,它们有点不同,所以我无法申请。我需要在循环之前将结果存储在变量中。再次感谢。
答案 0 :(得分:81)
以下是将find
的输出转换为bash
数组的一种解决方案:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
这很棘手,因为通常,文件名可以包含空格,换行符和其他脚本恶意字符。使用find
并使文件名彼此安全分离的唯一方法是使用-print0
打印以空字符分隔的文件名。如果bash的readarray
/ mapfile
函数支持以空值分隔的字符串,但这些字符串并不重要,这不会带来太多不便。 Bash的read
做了,这导致我们进入上面的循环。
第一行创建一个空数组:array=()
每次执行read
语句时,都会从标准输入中读取以空值分隔的文件名。 -r
选项告诉read
单独留下反斜杠字符。 -d $'\0'
告诉read
输入将以空值分隔。由于我们将名称省略为read
,因此shell会将输入设置为默认名称: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标准的一部分。
以下命令创建一个shell变量,而不是shell数组:
array=`find . -name "${input}"`
如果要创建数组,则需要在find的输出周围放置parens。所以天真地,人们可以:
array=(`find . -name "${input}"`) # don't do this
问题是shell对find
的结果执行了单词拆分,因此无法保证数组的元素符合您的要求。
答案 1 :(得分:13)
如果您使用bash
4或更高版本,则可以将find
替换为
shopt -s globstar nullglob
array=( **/*"$input"* )
**
启用的globstar
模式匹配0个或更多目录,允许模式匹配当前目录中的任意深度。如果没有nullglob
选项,模式(在参数扩展之后)将按字面处理,因此如果没有匹配,您将拥有一个包含单个字符串而不是空数组的数组。
如果您想遍历隐藏目录(如dotglob
)并匹配隐藏文件(如.ssh
),也可以将.bashrc
选项添加到第一行。
答案 2 :(得分:8)
你可以尝试类似的东西
array=(`find . -type f | sort -r | head -2`)
,为了打印数组值,您可以尝试使用echo "${array[*]}"
之类的内容
答案 3 :(得分:6)
Bash 4.4在-d
/ readarray
中引入了mapfile
选项,因此现在可以用
readarray -d '' array < <(find . -name "$input" -print0)
用于使用任意文件名(包括空格,换行符和通配符)的方法。
从manual(忽略其他选项):
mapfile [-d delim] [array]
-d
delim
的第一个字符用于终止每条输入行,而不是换行符。如果delim
为空字符串,则mapfile
将在读取NUL字符时终止一行。
readarray
只是mapfile
的同义词。
答案 4 :(得分:1)
在bash中,$(<any_shell_cmd>)
有助于运行命令并捕获输出。将此IFS
传递给\n
作为分隔符有助于将其转换为数组。
IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
答案 5 :(得分:1)
以下内容似乎适用于macOS上的Bash和Z Shell。
答案 6 :(得分:-1)
你可以这样做:
#!/bin/bash
echo "input : "
read input
echo "searching file with this pattern '${input}' under present directory"
array=(`find . -name '*'${input}'*'`)
for i in "${array[@]}"
do :
echo $i
done
答案 7 :(得分:-1)
对我来说,这在cygwin上运行良好:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
echo "$nm"
done
这适用于空格,但不适用于目录名称中的双引号(&#34;)(无论如何都不允许在Windows环境中使用)。
请注意-printf选项中的空格。