我正在使用以下代码连接当前目录中的所有 pdf 文件:
find . -iname '*.pdf'|sort|xargs|xargs -I {} pdftk {} cat output union.pdf
第一次调用 xargs 会将 sort 的输出转换为一行,其中的项目用空格分隔。但结果如下:
Error: Unable to find file.
Error: Failed to open PDF file:
./001.pdf ./002.pdf ./003.pdf ./004.pdf ./007.pdf ./010.pdf ./031.pdf ./057.pdf ./077.pdf ./103.pdf ./131.pdf ./155.pdf ./179.pdf ./205.pdf ./233.pdf ./261.pdf ./285.pdf ./313.pdf ./331.pdf ./357.pdf ./383.pdf ./411.pdf
Errors encountered. No output created.
Done. Input errors, so no output created.
xargs 是否将带有引号的参数传递给 pdftk?如何防止这种情况?(空格、转义以及它们与命令交互的方式总是让我抓狂……)
答案1
xargs 是否将带有引号的参数传递给 pdftk?
是也不是,但从技术上讲不是。xargs 没有引用,pdftk 也没有取消引用。
Linux/Unix 中程序接收命令行参数的方式不是通过使用需要加引号和取消加引号的单个字符串 - 这正是面向用户的“命令 shell”语言的工作方式,引号由 shell 解释,而不是由程序本身解释。(这与 Windows 的做法相反。)
内部程序使用大批(/list/vector)字符串,其固有保留了每个元素的精确文本内容和分隔,因此它实际上并不使用引用或转义。(也就是说 - 除非您必须嵌套它,在这种情况下它会回到字符串引用和解析,正如您将在下面看到的......)
例如,您的命令行被解析为此(例如,使用类似 C 的数组语法,但引号实际上不是字符串的一部分):
1. {"find", ".", "-iname", "*.pdf", NULL}
2. {"sort", NULL}
3. {"xargs", NULL}
4. {"xargs", "-I", "{}", "pdftk", "{}", "cat", "output", "union.pdf", NULL}
└─xargs uses these elements as the command─┘
因此,当 xargs 读取一行输入时(因为 -I 将其设置为逐行模式),它会替换符号{}
在每个单独的元素中使用输入行,不以任何方式重新排列元素。然后它要求操作系统运行结果:
{"pdftk", "./001.pdf ./002.pdf ./003.pdf …", "cat", "output", "union.pdf", NULL}
因此,您需要采用一种不同于xargs -I
单独行动的方式来实现这一目标。
例如,你可以问xargs 运行 shell – 然后它将按照您期望从 shell 中得到的方式解释/拆分/取消引用输入:
find … | sort | xargs | xargs -I {} bash -c "pdftk {} cat output union.pdf"
-c 后面的元素将变为
pdftk ./001.pdf ./002.pdf … cat output union.pdf
,并且 bash 将按预期将其拆分为单词。(但请注意,由于 xargs 不执行引用,因此这将拆分恰好包含空格的文件名,并且当文件名包含特殊字符时会产生奇怪的结果。)您可以使用 shell 的“进程替换”功能:
pdftk $(find … | sort) cat output union.pdf
这将在任何空格处分割结果文本(就像
$var
变量扩展一样)。行不需要先连接起来。但它在文件名包含空格时会出现同样的问题,在特殊字符时出现的问题会稍微少一些。受到推崇的:您可以完全避免使用“find”和“xargs”,而直接使用交互式 shell 的内置通配符匹配:
pdftk *.pdf cat output union.pdf
普通的 * 不是递归的,但在 Bash 或 zsh 中也有 **,它是递归模式:
shopt -s globstar # enable the feature (only needed in bash) pdftk **/*.pdf cat output union.pdf
(匹配结果将始终进行排序,至少在使用 POSIX sh 语言的 shell 中是如此。并且由于 shell 直接将每个文件名扩展为单独的命令行元素,因此即使文件名不常用,也不会出现任何引用问题。)