带有引号、空格的文件导致 xargs 出现不良行为

Files with quotes, spaces causing bad behavior from xargs

我想 find 一些文件并使用管道命令计算 shasum

find . -type f | xargs shasum

但是我的目录中有带引号的文件,例如名为

的文件
file with "special" characters.txt

管道输出如下所示:

user@home ~ $ find . -type f | xargs shasum
da39a3ee5e6b4b0d3255bfef95601890afd80709  ./empty1.txt
da39a3ee5e6b4b0d3255bfef95601890afd80709  ./empty2.txt
da39a3ee5e6b4b0d3255bfef95601890afd80709  ./empty3.txt
shasum: ./file: 
shasum: with: No such file or directory
shasum: special: No such file or directory
shasum: characters.txt: No such file or directory
25ea78ccd362e1903c4a10201092edeb83912d78  ./file1.txt
25ea78ccd362e1903c4a10201092edeb83912d78  ./file2.txt

文件名中的引号会产生问题。

如何告诉 shasum 正确处理文件?

简短的解释是 xargs 被广泛认为是设计错误,除非 使用标准扩展来禁用其尝试解析和遵守的行为在其输入中引用和转义内容。有关详细信息,请参阅 the xargs section of UsingFind


使用 NUL 分隔流

在具有 GNU 或现代 BSD 扩展(包括 MacOS X)的系统上,您可以(并且应该)用 NUL 分隔 find:

的输出
find . -type f -print0 | xargs -0 shasum --

使用find -exec

就是说,您可以通过完全符合现代 (~2006) POSIX:

的方式让 xargs 完全脱离循环,从而做得更好
find . -type f -exec shasum -- '{}' +

请注意,-- 参数指定 shasum 所有以后的参数都是文件名。如果您使用 find * -type f ...,那么您可能会得到以破折号开头的结果;使用 -- 可确保此结果不会被解释为一组选项。


使用换行符(及其安全风险)

如果您有 GNU xargs,但没有 以 NUL 分隔的输入流的选项,那么 xargs -d $'\n'(在诸如 bash 与 ksh 扩展)将避免引用和转义行为:

xargs -d $'\n' shasum -- <files.txt

但是,这是次优的,因为换行文字实际上可能存在于文件名中,因此无法区分分隔两个名称的换行符和作为实际名称一部分的换行符。考虑以下场景:

mkdir -p ./file.txt$'\n'/etc/passwd$'\n'/
touch ./file.txt$'\n'/etc/passwd$'\n'file.txt file.txt
find . -type f | xargs -d $'\n' shasum --

这将产生类似于以下内容的输出:

da39a3ee5e6b4b0d3255bfef95601890afd80709  ./file.txt
da39a3ee5e6b4b0d3255bfef95601890afd80709  ./file.txt
c0c71bac843a3ec7233e99e123888beb6da8fbcf  /etc/passwd
da39a3ee5e6b4b0d3255bfef95601890afd80709  file.txt

...因此允许可以控制文件名的攻击者为任意文件之外的预期目录结构添加到您的输出。