如何在Bash中将查找命令结果存储为数组

时间:2021-01-02 19:31:13

I am trying to save find result as arrays. Here is my code:

我正在尝试保存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

I get 2 .txt files under current directory. So I expect '2' as result of ${len}. However, it prints 1. The reason why is it takes all result of find as one elements. How can I fix this? Thanks.

我在当前目录下有2 .txt文件。因此,我希望'2'作为${len}的结果。然而,它打印1。原因是它将所有查找结果作为一个元素。我该怎么解决这个问题呢?谢谢。

P.S I found several solutions in * about similar problem. However, it is little bit different so I can apply to mine. I need to store result to variable before loop. Thanks again.

P。我在*上找到了几个类似的问题。但是,它有点不同,所以我可以用在我的上面。我需要在循环之前将结果存储到变量中。再次感谢。

6 个解决方案

#1


51  

Here is one solution for getting the output of find into a bash array:

这里有一种方法,可以将find的输出结果输出到bash数组中:

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

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.

这很棘手,因为一般来说,文件名可以有空格、新行和其他与脚本不兼容的字符。使用find并使文件名安全地彼此分离的唯一方法是使用-print0,它打印以空字符分隔的文件名。如果bash的readarray/mapfile函数支持空分隔的字符串,但它们不支持,那么这不会有多大的麻烦。Bash的read做到了,并将我们引向上面的循环。

How it works

  1. The first line creates an empty array: array=()

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

  2. 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 $'\0' 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.

    每次执行read语句时,从标准输入中读取一个空分隔的文件名。-r选项告诉read不要使用反斜杠字符。-d $'\0'告诉read输入将被空分隔。由于省略了要读取的名称,shell将输入放入默认名称:REPLY。

  3. The array+=("$REPLY") statement appends the new file name to the array array.

    数组+=(“$REPLY”)语句将新文件名附加到数组数组中。

  4. The final line combines redirection and command substitution to provide the output of find to the standard input of the while loop.

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

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 $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

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).

在上面,find的输出存储在一个临时文件中,该文件作为while循环的标准输入。过程替换的思想是使这些临时文件变得不必要。因此,与其让while循环从tmpfile中获取stdin,不如让它从<(find)中获取stdin。输入- name $ { } -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.

过程替换是非常有用的。在命令想要从文件中读取的许多地方,您可以指定进程替换,<(…),而不是文件名。有一个类似的形式,>(…),可以用来替代命令想要写入文件的文件名。

Like arrays, process substitution is a feature of bash and other advanced shells. It is not part of the POSIX standard.

与数组一样,进程替换是bash和其他高级shell的一个特性。它不是POSIX标准的一部分。

Additional notes

The following command creates a shell variable, not a shell array:

以下命令创建shell变量,而不是shell数组:

array=`find . -name "${input}"`

If you wanted to create an array, you would need to put parens around the output of find. So, naively, one could:

如果您想要创建一个数组,您需要在find的输出附近放置参数。所以,天真,一个可能:

array=(`find . -name "${input}"`)  # don't do this

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.

问题是shell对find结果执行分词,这样数组的元素就不能保证是您想要的。

#2


9  

If you are using bash 4 or later, you can replace your use of find with

如果您正在使用bash 4或更高版本,可以使用find替换您的使用

shopt -s globstar nullglob
array=( **/*"$input"* )

The ** pattern enabled by globstar matches 0 or more directories, allowing the pattern to match to an arbitrary depth in the current directory. Without the nullglob option, the pattern (after parameter expansion) is treated literally, so with no matches you would have an array with a single string rather than an empty array.

globstar启用的**模式匹配0个或多个目录,允许模式匹配当前目录中的任意深度。如果没有nullglob选项,模式(在参数展开之后)就会得到字面上的处理,因此如果没有匹配,就会有一个具有单个字符串的数组,而不是一个空数组。

Add the dotglob option to the first line as well if you want to traverse hidden directories (like .ssh) and match hidden files (like .bashrc) as well.

如果您想要遍历隐藏目录(如.ssh)并匹配隐藏文件(如.bashrc),也可以将dotglob选项添加到第一行。

#3


4  

you can try something like

你可以试试

array=(`find . -type f | sort -r | head -2`)
, and in order to print the array values , you can try something like echo "${array[*]}"

#4


0  

In bash, $(<any_shell_cmd>) helps to run a command and capture the output. Passing this to IFS with \n as delimeter helps to convert that as to an array.

在bash中,$( )帮助运行命令并捕获输出。用\n作为递归表将其传递给IFS有助于将其转换为数组。

IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")

#5


-1  

You could do like this:

你可以这样做:

#!/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

#6


-1  

For me, this worked fine on cygwin:

对我来说,这对cygwin很有效:

declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
    echo "$nm"
done

This works with spaces, but not with double quotes (") in the directory names.

这适用于空格,但不适用于目录名中的双引号(")。

Beware the space in the -printf option.

注意-printf选项中的空格。

#1


51  

Here is one solution for getting the output of find into a bash array:

这里有一种方法,可以将find的输出结果输出到bash数组中:

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

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.

这很棘手,因为一般来说,文件名可以有空格、新行和其他与脚本不兼容的字符。使用find并使文件名安全地彼此分离的唯一方法是使用-print0,它打印以空字符分隔的文件名。如果bash的readarray/mapfile函数支持空分隔的字符串,但它们不支持,那么这不会有多大的麻烦。Bash的read做到了,并将我们引向上面的循环。

How it works

  1. The first line creates an empty array: array=()

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

  2. 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 $'\0' 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.

    每次执行read语句时,从标准输入中读取一个空分隔的文件名。-r选项告诉read不要使用反斜杠字符。-d $'\0'告诉read输入将被空分隔。由于省略了要读取的名称,shell将输入放入默认名称:REPLY。

  3. The array+=("$REPLY") statement appends the new file name to the array array.

    数组+=(“$REPLY”)语句将新文件名附加到数组数组中。

  4. The final line combines redirection and command substitution to provide the output of find to the standard input of the while loop.

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

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 $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

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).

在上面,find的输出存储在一个临时文件中,该文件作为while循环的标准输入。过程替换的思想是使这些临时文件变得不必要。因此,与其让while循环从tmpfile中获取stdin,不如让它从<(find)中获取stdin。输入- name $ { } -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.

过程替换是非常有用的。在命令想要从文件中读取的许多地方,您可以指定进程替换,<(…),而不是文件名。有一个类似的形式,>(…),可以用来替代命令想要写入文件的文件名。

Like arrays, process substitution is a feature of bash and other advanced shells. It is not part of the POSIX standard.

与数组一样,进程替换是bash和其他高级shell的一个特性。它不是POSIX标准的一部分。

Additional notes

The following command creates a shell variable, not a shell array:

以下命令创建shell变量,而不是shell数组:

array=`find . -name "${input}"`

If you wanted to create an array, you would need to put parens around the output of find. So, naively, one could:

如果您想要创建一个数组,您需要在find的输出附近放置参数。所以,天真,一个可能:

array=(`find . -name "${input}"`)  # don't do this

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.

问题是shell对find结果执行分词,这样数组的元素就不能保证是您想要的。

#2


9  

If you are using bash 4 or later, you can replace your use of find with

如果您正在使用bash 4或更高版本,可以使用find替换您的使用

shopt -s globstar nullglob
array=( **/*"$input"* )

The ** pattern enabled by globstar matches 0 or more directories, allowing the pattern to match to an arbitrary depth in the current directory. Without the nullglob option, the pattern (after parameter expansion) is treated literally, so with no matches you would have an array with a single string rather than an empty array.

globstar启用的**模式匹配0个或多个目录,允许模式匹配当前目录中的任意深度。如果没有nullglob选项,模式(在参数展开之后)就会得到字面上的处理,因此如果没有匹配,就会有一个具有单个字符串的数组,而不是一个空数组。

Add the dotglob option to the first line as well if you want to traverse hidden directories (like .ssh) and match hidden files (like .bashrc) as well.

如果您想要遍历隐藏目录(如.ssh)并匹配隐藏文件(如.bashrc),也可以将dotglob选项添加到第一行。

#3


4  

you can try something like

你可以试试

array=(`find . -type f | sort -r | head -2`)
, and in order to print the array values , you can try something like echo "${array[*]}"

#4


0  

In bash, $(<any_shell_cmd>) helps to run a command and capture the output. Passing this to IFS with \n as delimeter helps to convert that as to an array.

在bash中,$( )帮助运行命令并捕获输出。用\n作为递归表将其传递给IFS有助于将其转换为数组。

IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")

#5


-1  

You could do like this:

你可以这样做:

#!/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

#6


-1  

For me, this worked fine on cygwin:

对我来说,这对cygwin很有效:

declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
    echo "$nm"
done

This works with spaces, but not with double quotes (") in the directory names.

这适用于空格,但不适用于目录名中的双引号(")。

Beware the space in the -printf option.

注意-printf选项中的空格。