如何存储"发现"命令结果作为Bash中的数组

时间:2014-04-29 06:07:10

标签: arrays bash variables find

我正在尝试将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上找到了几个关于类似问题的解决方案。但是,它们有点不同,所以我无法申请。我需要在循环之前将结果存储在变量中。再次感谢。

8 个答案:

答案 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做了,这导致我们进入上面的循环。

如何运作

  1. 第一行创建一个空数组:array=()

  2. 每次执行read语句时,都会从标准输入中读取以空值分隔的文件名。 -r选项告诉read单独留下反斜杠字符。 -d $'\0'告诉read输入将以空值分隔。由于我们将名称省略为read,因此shell会将输入设置为默认名称:REPLY

  3. array+=("$REPLY")语句将新文件名附加到数组array

  4. 最后一行结合重定向和命令替换,以将find的输出提供给while循环的标准输入。

  5. 为什么要使用流程替换?

    如果我们没有使用进程替换,那么循环可以写成:

    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选项中的空格。