将 xargs 与 pdftk 一起使用

Miz*_*zar 6 imagemagick xargs

我正在使用以下代码连接当前目录中的所有 pdf 文件:

find . -iname '*.pdf'|sort|xargs|xargs -I {} pdftk {} cat output union.pdf
Run Code Online (Sandbox Code Playgroud)

第一次调用 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.
Run Code Online (Sandbox Code Playgroud)

xargs 是否将参数传递给带有周围引号的 pdftk?如何防止这种情况?(空格、转义以及它们与命令交互的方式总是让我发疯......)

use*_*686 9

xargs 是否将参数传递给带有周围引号的 pdftk?

是和否,但技术上不是。xargs 不引用,pdftk 也不引用。

程序在 Linux/Unix 中接收命令行参数的方式不是使用需要加引号和不加引号的单个字符串——这就是面向用户的“命令外壳”语言的工作方式,引号由你的外壳解释,不是通过程序本身。(这与 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??
Run Code Online (Sandbox Code Playgroud)

因此,当 xargs 读取一行输入时(因为 -I 将其设置为逐行模式),它会将{} 每个单独元素中的符号替换为输入行,而不会以任何方式重新排列元素。然后它要求操作系统运行结果:

{"pdftk", "./001.pdf ./002.pdf ./003.pdf …", "cat", "output", "union.pdf", NULL}
Run Code Online (Sandbox Code Playgroud)

因此,您需要一种不同的方式来实现这一目标,而不是xargs -I单独使用。

  • 例如,您可以要求xargs 运行一个 shell——然后它会以与您期望从 shell 相同的方式解释/拆分/取消引用输入:

    find … | sort | xargs | xargs -I {} bash -c "pdftk {} cat output union.pdf"
    
    Run Code Online (Sandbox Code Playgroud)

    -c 后面的元素将变为pdftk ./001.pdf ./002.pdf … cat output union.pdfbash 将其拆分为预期的单词。(但请注意,因为 xargs 不进行引用,这将拆分碰巧包含空格的文件名,并且当文件名包含特殊字符时会产生奇怪的结果。)

  • 您可以使用 shell 的“进程替换”功能:

    pdftk $(find … | sort) cat output union.pdf
    
    Run Code Online (Sandbox Code Playgroud)

    这将在任何空白处拆分结果文本(就像$var变量扩展一样)。这些行不需要先加入。但是文件名中包含空格也会有同样的问题,特殊字符的问题会少一些。

  • 推荐:您可以完全避免使用 'find' 和 'xargs' 并直接使用交互式 shell 的内置通配符匹配:

    pdftk *.pdf cat output union.pdf
    
    Run Code Online (Sandbox Code Playgroud)

    普通 * 不是递归的,但在 Bash 或 zsh 中你也有 ** 这是递归模式:

    shopt -s globstar                       # enable the feature (only needed in bash)
    
    pdftk **/*.pdf cat output union.pdf
    
    Run Code Online (Sandbox Code Playgroud)

    (匹配结果总是会被排序,至少在使用 POSIX sh 语言的 shell 中是这样。并且因为 shell 直接将每个文件名扩展为一个单独的命令行元素,所以根本没有引用问题,即使是不常用的文件名。)