递归遍历目录中的文件


15

通过以下方式可以轻松地递归遍历目录中的文件:

find . -type f -exec bar {} \;

但是,上述方法不适用于更复杂的事情,在这些事情中,需要完成许多条件分支,循环等。我曾经在上面使用它:

while read line; do [...]; done < <(find . -type f)

但是,这似乎不适用于包含晦涩字符的文件:

$ touch $'a\nb'
$ find . -type f
./a?b

有没有替代方法可以很好地处理此类晦涩的字符?


1
find ... -exec bash -c 'echo filename is in \$0: "$0"' {} \;是一种更好的方法。
jw013 2014年

通过将更read line改为,您可以解决此问题并保留原始设计IFS= read -r line。唯一会破坏它的字符是换行符。
Patrick

1
@Patrick,但文件名可以包含换行符。这就是为什么-d $'\0'更好。
godlygeek 2014年

Answers:


7

安全的find另一种用途:

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(这适用于任何POSIX find,但是shell部分需要bash。使用* BSD和GNU find,可以使用-print0代替-exec printf '%s\0' {} +,它会稍微快一些。)

这样就可以在循环中使用标准输入,并且可以在任何路径上使用。


1
因为我必须查找它:“读取...如果未提供名称,则将读取的行分配给变量REPLY。” 所以do echo "Filename is '$REPLY'"
安德鲁

9

这样做很简单:

find -exec sh -c 'inline script "$0"' {} \;

要么...

find -exec executable_script {} \;

5

最简单(至今仍安全)的方法是使用外壳程序:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

要将以上内容递归到子目录(以bash表示),可以使用globstar选项;还设置dotglob为匹配名称以.:开头的文件

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

请注意,直到bash 4.2为止,都会**/递归到目录的符号链接。从bash 4.3开始,**/仅递归到目录,例如find

另一个常见的解决方案是find -print0xargs -0

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

请注意,h:/g实际上是正确的,因为文件名包含\r


4

这是一个有点困难,可移植做你读循环,但对于具体的bash,那么你可以尝试像这样

相关部分:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

这指示find打印输出以NUL字符(0x00)分隔的输出,并read获取以NUL分隔的行(-d $'\0')而不处理反斜杠作为其他字符(-r)的转义符,并且不对行进行任何字分割(IFS=)。由于0x00是Unix中文件名或路径中不能出现的字节,因此这应该可以解决所有奇怪的文件名问题。


1
-d ''等同于-d $'\0'
l0b0 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.