我如何存储“查找"商品?命令结果作为Bash中的数组 [英] How can I store the "find" command results as an array in Bash

查看:88
本文介绍了我如何存储“查找"商品?命令结果作为Bash中的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将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文件. 因此,我希望${len}的结果为"2".但是,它打印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?

P.S
我在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.

推荐答案

Linux用户的2020更新:

如果您具有bash的最新版本(4.4-alpha或更高版本)(如在Linux上一样),则应使用如果您使用的是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 $'\0'; 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版本,请参见下面的更新.]

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

每次执行read语句时,都会从标准输入中读取以空分隔的文件名. -r选项告诉read保留反斜杠字符. -d $'\0'告诉read输入将以空分隔.由于我们将名称省略为read,因此外壳程序会将输入内容放入默认名称: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 $'\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.

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

在上面的find的输出存储在一个临时文件中,该文件用作while循环的标准输入.进程替换的想法是使这些临时文件变得不必要.因此,除了让while循环从tmpfile获取其stdin之外,我们还可以让它从<(find . -name ${input} -print0)获取其stdin.

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

shopt -s lastpipe告诉bash在当前shell(而不是后台)中的管道中运行最后一个命令.这样,array在管道完成后仍然存在.因为lastpipe仅在关闭作业控制后才生效,所以我们运行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

问题是外壳程序对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 $'\0' array < <(find . -name "${input}" -print0)

有关此问题的更多信息,请参阅(并投票) Benjamin W.的回答.

For more information on this, please see (and upvote) Benjamin W.'s answer.

这篇关于我如何存储“查找"商品?命令结果作为Bash中的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆