递归遍历目录中的文件

use*_*000 19 bash directory recursive

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

find . -type f -exec bar {} \;
Run Code Online (Sandbox Code Playgroud)

但是,以上不适用于更复杂的事情,其中​​需要完成很多条件分支、循环等。我曾经将它用于上述目的:

while read line; do [...]; done < <(find . -type f)
Run Code Online (Sandbox Code Playgroud)

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

$ touch $'a\nb'
$ find . -type f
./a?b
Run Code Online (Sandbox Code Playgroud)

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

l0b*_*0b0 10

safe 的find另一种用途:

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

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

这使得在循环中使用标准输入成为可能,并且它适用于任何路径。

  • 因为我必须查找它:“读取...如果没有提供名称,读取的行将分配给变量 REPLY。” 所以`做回声“文件名是'$ REPLY'”` (2认同)

mik*_*erv 9

这样做很简单:

find -exec sh -c 'inline script "$0"' {} \;
Run Code Online (Sandbox Code Playgroud)

或者...

find -exec executable_script {} \;
Run Code Online (Sandbox Code Playgroud)


ter*_*don 6

最简单(但安全)的方法是使用 shell globbing:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:
Run Code Online (Sandbox Code Playgroud)

要使上述递归进入子目录(在 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:
Run Code Online (Sandbox Code Playgroud)

请注意,直到 bash 4.2,**/递归到目录的符号链接。从 bash 4.3 开始,**/只递归到目录中,比如find.

另一个常见的解决方案是使用find -print0with xargs -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:
Run Code Online (Sandbox Code Playgroud)

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


god*_*eek 5

可移植地执行读取循环有点困难,但特别是对于 bash,您可以尝试这样的操作

相关部分:

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

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