捕获 find 的输出。-print0 到 bash 数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1116992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capturing output of find . -print0 into a bash array
提问by Idris
Using find . -print0
seems to be the only safe way of obtaining a list of files in bash due to the possibility of filenames containing spaces, newlines, quotation marks etc.
find . -print0
由于文件名可能包含空格、换行符、引号等,因此使用似乎是在 bash 中获取文件列表的唯一安全方法。
However, I'm having a hard time actually making find's output useful within bash or with other command line utilities. The only way I have managed to make use of the output is by piping it to perl, and changing perl's IFS to null:
但是,我很难在 bash 或其他命令行实用程序中实际使 find 的输出有用。我设法利用输出的唯一方法是将其通过管道传输到 perl,并将 perl 的 IFS 更改为 null:
find . -print0 | perl -e '$/="find . | wc -l
"; @files=<>; print $#files;'
This example prints the number of files found, avoiding the danger of newlines in filenames corrupting the count, as would occur with:
此示例打印找到的文件数,避免文件名中的换行符破坏计数的危险,就像以下情况一样:
find . -print0 | ( IFS=$'unset a i
while IFS= read -r -d $'unset a i
while IFS= read -r -u3 -d $'find . -print0 | xargs -r0 do_something_useful
' file; do
a[i++]="$file" # or however you want to process each file
done 3< <(find /tmp -type f -print0)
' file; do
a[i++]="$file" # or however you want to process each file
done < <(find /tmp -type f -print0)
' ; array=( $( cat ) ) ; echo ${#array[@]} )
As most command line programs do not support null-delimited input, I figure the best thing would be to capture the output of find . -print0
in a bash array, like I have done in the perl snippet above, and then continue with the task, whatever it may be.
由于大多数命令行程序不支持以空分隔的输入,我认为最好的办法是find . -print0
在 bash 数组中捕获输出,就像我在上面的 perl 片段中所做的那样,然后继续执行任务,不管它可能是。
How can I do this?
我怎样才能做到这一点?
This doesn't work:
这不起作用:
#!/bin/bash
printf "%s" "$@" | base64
A much more general question might be: How can I do useful things with lists of files in bash?
一个更普遍的问题可能是:如何使用 bash 中的文件列表做有用的事情?
回答by Gordon Davisson
Shamelessly stolen from Greg's BashFAQ:
从Greg 的 BashFAQ无耻地窃取:
for i in `find -type f -exec base64str '{}' \;`
do
file="`echo -n "$i" | base64 -d`"
# do something with file
done
Note that the redirection construct used here (cmd1 < <(cmd2)
) is similar to, but not quite the same as the more usual pipeline (cmd2 | cmd1
) -- if the commands are shell builtins (e.g. while
), the pipeline version executes them in subshells, and any variables they set (e.g. the array a
) are lost when they exit. cmd1 < <(cmd2)
only runs cmd2 in a subshell, so the array lives past its construction. Warning: this form of redirection is only available in bash, not even bash in sh-emulation mode; you must start your script with #!/bin/bash
.
请注意,此处使用的重定向构造 ( cmd1 < <(cmd2)
) 与更常见的管道 ( cmd2 | cmd1
)类似,但并不完全相同——如果命令是 shell 内置命令 (例如while
),则管道版本在子 shell 中执行它们,以及它们设置的任何变量(例如数组a
)在退出时丢失。 cmd1 < <(cmd2)
只在子 shell 中运行 cmd2,所以数组在它的构造之后仍然存在。警告:这种形式的重定向仅在 bash 中可用,甚至在 sh-emulation 模式下的 bash 中都不可用;你必须用#!/bin/bash
.
Also, because the file processing step (in this case, just a[i++]="$file"
, but you might want to do something fancier directly in the loop) has its input redirected, it cannot use any commands that might read from stdin. To avoid this limitation, I tend to use:
此外,由于文件处理步骤(在这种情况下,只是a[i++]="$file"
,但您可能想直接在循环中做一些更有趣的事情)将其输入重定向,因此它不能使用任何可能从标准输入读取的命令。为了避免这种限制,我倾向于使用:
mapfile -d '' ary < <(find . -print0)
...which passes the file list via unit 3, rather than stdin.
...它通过单元 3 传递文件列表,而不是标准输入。
回答by Balázs Pozsár
Maybe you are looking for xargs:
也许您正在寻找 xargs:
find /DIR -type f -print0 | tr -dc 'find . -exec echo ';' | wc -l
' | wc -c
The option -L 1 could be useful for you too, which makes xargs exec do_something_useful with only 1 file argument.
选项 -L 1 也可能对您有用,这使得 xargs exec do_something_useful 只有 1 个文件参数。
回答by zstegi
The main problem is, that the delimiter NUL (\0) is useless here, because it isn't possible to assign IFS a NUL-value. So as good programmers we take care, that the input for our program is something it is able to handle.
主要问题是,分隔符 NUL (\0) 在这里没有用,因为不可能为 IFS 分配 NUL 值。因此,作为优秀的程序员,我们要注意,我们程序的输入是它能够处理的。
First we create a little program, which does this part for us:
首先我们创建一个小程序,它为我们做这部分:
i=0;
for f in *; do
array[$i]="$f"
((i++))
done
...and call it base64str (don't forget chmod +x)
...并称之为base64str(不要忘记chmod +x)
Second we can now use a simple and straightforward for-loop:
其次,我们现在可以使用一个简单直接的 for 循环:
for ((i = $i - 1; i >= 0; i--)); do
ls -al "${array[$i]}"
done
So the trick is, that a base64-string has no sign which causes trouble for bash - of course a xxd or something similar can also do the job.
所以诀窍是,base64-string 没有导致 bash 麻烦的迹象 - 当然 xxd 或类似的东西也可以完成这项工作。
回答by gniourf_gniourf
Since Bash 4.4, the builtin mapfile
has the -d
switch (to specify a delimiter, similar to the -d
switch of the read
statement), and the delimiter can be the null byte. Hence, a nice answer to the question in the title
从Bash 4.4开始,内置mapfile
有-d
switch(用来指定定界符,类似于语句的-d
switch read
),定界符可以是空字节。因此,标题中问题的一个很好的答案
Capturing output of
find . -print0
into a bash array
将 的输出捕获
find . -print0
到 bash 数组中
is:
是:
man ruby | less -p 777
IFS=$'7'
#array=( $(find ~ -maxdepth 1 -type f -exec printf "%s7" '{}' \; 2>/dev/null) )
array=( $(find ~ -maxdepth 1 -type f -exec printf "%s7" '{}' + 2>/dev/null) )
echo ${#array[@]}
printf "%s\n" "${array[@]}" | nl
echo "${array[0]}"
IFS=$' \t\n'
回答by gniourf_gniourf
Yet another way of counting files:
另一种计算文件的方法:
STYLE="$HOME/.fluxbox/styles/"
declare -a array1
LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f`
echo $LISTING
array1=( `echo $LISTING`)
TAR_SOURCE=`echo ${array1[@]}`
#tar czvf ~/FluxieStyles.tgz $TAR_SOURCE
回答by Balázs Pozsár
You can safely do the count with this:
您可以安全地进行计数:
files=(*) # put files in current directory into an array
i=0
for file in "${files[@]}"
do
echo "File ${i}: ${file}" # do something useful
let i++
done
(It prints a newline for every file/dir found, and then count the newlines printed out...)
(它为找到的每个文件/目录打印一个换行符,然后计算打印出的换行符...)
回答by Stephan202
I think more elegant solutions exists, but I'll toss this one in. This will also work for filenames with spaces and/or newlines:
我认为存在更优雅的解决方案,但我会扔掉这个。这也适用于带有空格和/或换行符的文件名:
echo ${#files[@]}
You can then e.g. list the files one by one (in this case in reverse order):
然后,您可以例如一个一个地列出文件(在这种情况下以相反的顺序):
##代码##This pagegives a nice example, and for more see Chapter 26in the Advanced Bash-Scripting Guide.
这个页面给出了一个很好的例子,更多信息请参见Advanced Bash-Scripting Guide中的第 26 章。
回答by Stephan202
Avoid xargs if you can:
如果可以,请避免使用 xargs:
##代码##回答by Stephan202
I am new but I believe that this an answer; hope it helps someone:
我是新手,但我相信这是一个答案;希望它可以帮助某人:
##代码##回答by Paused until further notice.
This is similar to Stephan202's version, but the files (and directories) are put into an array all at once. The for
loop here is just to "do useful things":
这类似于 Stephan202 的版本,但文件(和目录)是一次性放入一个数组中的。for
这里的循环只是为了“做有用的事情”:
To get a count:
要计数:
##代码##