递归遍历目录中的文件

递归遍历目录中的文件

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

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

然而,上面的方法不适用于更复杂的事情,因为需要完成很多条件分支、循环等。我曾经在上面使用过这个:

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

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

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

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

答案1

还有一个用途安全的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' {} +,它会稍微快一些。)

这使得在循环内使用标准输入成为可能,并且它可以与任何小路。

答案2

这样做很简单:

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

或者...

find -exec executable_script {} \;

答案3

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

$ 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 -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:

请注意,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 中的文件名或路径中的字节,因此这应该可以处理所有奇怪的文件名问题。

相关内容