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' {} +
,它会稍微快一点。)
这使得在循环中使用标准输入成为可能,并且它适用于任何路径。
这样做很简单:
find -exec sh -c 'inline script "$0"' {} \;
Run Code Online (Sandbox Code Playgroud)
或者...
find -exec executable_script {} \;
Run Code Online (Sandbox Code Playgroud)
最简单(但安全)的方法是使用 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 -print0
with 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
.
可移植地执行读取循环有点困难,但特别是对于 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 中的文件名或路径中的字节,因此这应该可以处理所有奇怪的文件名问题。