我正在使用以下代码连接当前目录中的所有 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?如何防止这种情况?(空格、转义以及它们与命令交互的方式总是让我抓狂……)
是也不是,但从技术上讲不是。xargs 不引用,pdftk 也不取消引用。
程序在 Linux/Unix 中接收命令行参数的方式不是使用需要被引用和取消引用的单个字符串——这正是面向用户的“命令 shell”语言的工作方式,引号由你的 shell 解释,不是通过程序本身。(这与 Windows 的做法相反。)
在内部程序是使用字符串数组(/list/vector) 启动的,它固有地保留了每个元素的确切文本内容和分隔,因此它实际上并没有首先使用引号或转义。(也就是说 - 除非你必须嵌套它,在这种情况下它会回到字符串引用和解析,正如你将在下面看到的......)
例如,您的命令行被解析成这样(例如使用类似 C 的数组语法,但引号实际上不是字符串的一部分):
因此,当 xargs 读取一行输入时(因为 -I 将其设置为逐行模式),它会用输入行替换
{}
每个单独元素中的符号,而不会以任何方式重新排列元素。然后它要求操作系统运行结果:所以你需要一种不同于
xargs -I
单独的方式来实现这一目标。例如,您可以要求xargs 运行一个 shell——然后它将以您期望从 shell 中获得的相同方式解释/拆分/取消引用输入:
-c 之后的元素将变为
pdftk ./001.pdf ./002.pdf … cat output union.pdf
bash 将按预期将其拆分为单词。(但请注意,因为 xargs 不使用引号,这将拆分恰好包含空格的文件名,并且当文件名包含特殊字符时会产生奇怪的结果。)您可以使用 shell 的“进程替换”功能:
这将在任何空白处拆分结果文本(就像
$var
变量扩展一样)。不需要先连接这些线。但是对于包含空格的文件名会有同样的问题,而对于特殊字符的问题会稍微少一些。推荐:你可以完全避免 'find' 和 'xargs' 并直接使用交互式 shell 的内置通配符匹配:
普通 * 不是递归的,但在 Bash 或 zsh 中你也有 ** 这是递归模式:
(匹配结果将始终排序,至少在使用 POSIX sh 语言的 shell 中是这样。并且因为 shell 直接将每个文件名扩展为单独的命令行元素,所以根本不会出现引用问题,即使是不常用的文件名也是如此。)