如何遍历find返回的文件名? [英] How to loop through file names returned by find?
问题描述
x=$(find .-name "*.txt")回声$x
如果我在 Bash shell 中运行上面的代码,我得到的是一个包含多个文件名的字符串,由空格分隔,而不是一个列表.
当然,我可以进一步用空格将它们分开以获得列表,但我相信有更好的方法来做到这一点.
那么循环遍历 find
命令的结果的最佳方法是什么?
TL;DR:如果你只是为了最正确的答案,你可能想要我的个人偏好(见本文底部):
>
#对每个文件执行一次`process`找 .-name '*.txt' -exec 进程 {} ;
如果您有时间,请通读其余部分以了解几种不同的方法以及其中大多数方法的问题.
完整答案:
最好的方法取决于您想做什么,但这里有几个选项.只要子树中的文件或文件夹的名称中没有空格,您就可以遍历文件:
for i in $x;do # 不推荐,会在空格处中断处理$i"完毕
稍微好一点,去掉临时变量x
:
for i in $(find -name *.txt);do # 不推荐,会在空格处中断处理$i"完毕
在可能的情况下使用 glob 要好得多.空白安全,用于当前目录中的文件:
for i in *.txt;do # 空格安全但不是递归的.处理$i"完毕
通过启用 globstar
选项,您可以对这个目录和所有子目录中的所有匹配文件进行 glob:
# 确保 globstar 已启用shopt -s globstar对于 **/*.txt 中的 i;do # 空格安全和递归处理$i"完毕
在某些情况下,例如如果文件名已经在一个文件中,你可能需要使用 read
:
# IFS= 确保它不会修剪前导和尾随空格# -r 防止解释 转义.而 IFS= 读取 -r 行;do # 空格安全的 EXCEPT 换行符处理$line"完成<文档名称
通过适当设置分隔符,
read
可以安全地与 find
结合使用:
查找.-name '*.txt' -print0 |而 IFS= read -r -d '' 行;做处理$line"完毕
对于更复杂的搜索,您可能希望使用 find
,或者使用它的 -exec
选项或使用 -print0 |xargs -0
:
#对每个文件执行一次`process`找 .-name *.txt -exec process {} ;# 将所有文件作为参数执行一次 `process`*:找 .-name *.txt -exec 进程 {} +# 使用 xargs*找 .-name *.txt -print0 |xargs -0 进程# 在每个文件名后使用带参数的 xargs(意味着每个文件名运行一次)找 .-name *.txt -print0 |xargs -0 -I{} 处理 {} 参数
find
也可以使用 -execdir
而不是 -exec
在运行命令之前 cd 到每个文件的目录,并且可以进行交互(在为每个文件运行命令之前提示)使用 -ok
而不是 -exec
(或 -okdir
而不是 -execdir
).
*:从技术上讲,find
和 xargs
(默认情况下)都将使用命令行中可以容纳的尽可能多的参数运行命令,次数为它需要通过所有文件.在实践中,除非您有大量文件,否则这无关紧要,并且如果您超出了长度但需要在同一命令行上全部使用,您是 SOL 会找到不同的方法.
x=$(find . -name "*.txt")
echo $x
if I run the above piece of code in Bash shell, what I get is a string containing several file names separated by blank, not a list.
Of course, I can further separate them by blank to get a list, but I'm sure there is a better way to do it.
So what is the best way to loop through the results of a find
command?
TL;DR: If you're just here for the most correct answer, you probably want my personal preference (see the bottom of this post):
# execute `process` once for each file
find . -name '*.txt' -exec process {} ;
If you have time, read through the rest to see several different ways and the problems with most of them.
The full answer:
The best way depends on what you want to do, but here are a few options. As long as no file or folder in the subtree has whitespace in its name, you can just loop over the files:
for i in $x; do # Not recommended, will break on whitespace
process "$i"
done
Marginally better, cut out the temporary variable x
:
for i in $(find -name *.txt); do # Not recommended, will break on whitespace
process "$i"
done
It is much better to glob when you can. White-space safe, for files in the current directory:
for i in *.txt; do # Whitespace-safe but not recursive.
process "$i"
done
By enabling the globstar
option, you can glob all matching files in this directory and all subdirectories:
# Make sure globstar is enabled
shopt -s globstar
for i in **/*.txt; do # Whitespace-safe and recursive
process "$i"
done
In some cases, e.g. if the file names are already in a file, you may need to use read
:
# IFS= makes sure it doesn't trim leading and trailing whitespace
# -r prevents interpretation of escapes.
while IFS= read -r line; do # Whitespace-safe EXCEPT newlines
process "$line"
done < filename
read
can be used safely in combination with find
by setting the delimiter appropriately:
find . -name '*.txt' -print0 |
while IFS= read -r -d '' line; do
process "$line"
done
For more complex searches, you will probably want to use find
, either with its -exec
option or with -print0 | xargs -0
:
# execute `process` once for each file
find . -name *.txt -exec process {} ;
# execute `process` once with all the files as arguments*:
find . -name *.txt -exec process {} +
# using xargs*
find . -name *.txt -print0 | xargs -0 process
# using xargs with arguments after each filename (implies one run per filename)
find . -name *.txt -print0 | xargs -0 -I{} process {} argument
find
can also cd into each file's directory before running a command by using -execdir
instead of -exec
, and can be made interactive (prompt before running the command for each file) using -ok
instead of -exec
(or -okdir
instead of -execdir
).
*: Technically, both find
and xargs
(by default) will run the command with as many arguments as they can fit on the command line, as many times as it takes to get through all the files. In practice, unless you have a very large number of files it won't matter, and if you exceed the length but need them all on the same command line, you're SOL find a different way.
这篇关于如何遍历find返回的文件名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!