我想将文件名通过管道传递给其他程序,但当名称包含空格时,它们都会窒息。
假设我有一个名为的文件。
foo bar
我怎样才能find
返回正确的名称?
显然我想要:
foo\ bar
或者:
"foo bar"
编辑:我不想经历xargs
,我想得到一个格式正确的字符串,find
以便我可以将文件名字符串直接通过管道传输到另一个程序。
答案1
正面:
find . -type f -exec sh -c '
for f do
: command "$f"
done
' sh {} +
有find
支持-print0
和xargs
支持-0
:
find . -type f -print0 | xargs -0 <command>
-0
选项告诉 xargs 使用 ASCII NUL 字符而不是空格来结束(分隔)文件名。
例子:
find . -maxdepth 1 -type f -print0 | xargs -0 ls -l
答案2
使用-print0
是一种选择,但并非所有程序都支持使用空字节分隔的数据流,因此您必须在某些情况下使用xargs
该-0
选项,正如 Gnouc 的回答所述。
另一种选择是使用find
's-exec
或-execdir
options。以下第一个将somecommand
一次提供一个文件名,而第二个将扩展为文件列表:
find . -type f -exec somecommand '{}' \;
find . -type f -exec somecommand '{}' +
您可能会发现在许多情况下使用通配符会更好。如果您有现代 shell(bash 4+、zsh、ksh),则可以使用globstar
( **
) 进行递归通配。在 bash 中,你必须这样设置:
shopt -s globstar
somecommand ./**/*.txt ## feeds all *.txt files to somecommand, recursively
我的 .bashrc 中有一行内容shopt -s globstar extglob
,因此它始终对我启用(扩展的 glob 也是如此,这也很有用)。
如果您不想要递归性,显然只需使用./*.txt
工作目录中的每个 *.txt 即可。find
具有一些非常有用的细粒度搜索功能,并且对于数以万计的文件是必需的(此时您将遇到 shell 的最大参数数量),但对于日常使用来说,它通常是不必要的。
答案3
因此,如果您不想使用xargs
(因此可能也不想使用parallel
),find
可以按以下方式逐行读取和处理输出:
find . -type f | while read x; do
# do something with $x
done
答案4
就我个人而言,我会使用-exec
find 操作来解决此类问题。或者,如果需要,xargs
允许并行执行。
然而,有一种方法可以find
生成 bash 可读的文件名列表。毫不奇怪,它使用-exec
and bash
,特别是命令的扩展printf
:
find ... -exec bash -c 'printf "%q " "$@"' printf {} ';'
然而,虽然这会正确打印出 shell 转义的单词,但它不能与 一起使用$(...)
,因为$(...)
不解释引号或转义。 (除非用引号括起来,否则的结果$(...)
会受到分词和路径名扩展的影响。)因此以下内容不会执行您想要的操作:
ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)
你需要做的是:
eval "ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)"
(请注意,我并没有真正尝试测试上述怪物。)
但那么你不妨这样做:
find ... -exec ls {} +