为什么在 shell 脚本(使用 pdftk)中注释掉这一行会导致问题?

Fah*_*tha 5 shell-script

考虑以下简单的 shell 脚本(使用pdftk):

#!/bin/sh    
echo "" | ps2pdf -sPAPERSIZE=a4 - blank.pdf

pdftk \
    A=blank.pdf \
    B=blank.pdf \
    C=blank.pdf \
    cat A C \
    output foo.pdf
Run Code Online (Sandbox Code Playgroud)

现在,如果我注释掉一行,就会出现古怪。这是修改后的脚本。

#!/bin/sh

echo "" | ps2pdf -sPAPERSIZE=a4 - blank.pdf

pdftk \
    A=blank.pdf \
#   B=blank.pdf \
    C=blank.pdf \
    cat A C \
    output foo.pdf
Run Code Online (Sandbox Code Playgroud)

输出看起来像:

Done.  Input errors, so no output created.
cat: A: No such file or directory
cat: C: No such file or directory
cat: output: No such file or directory
%PDF-1.4
%<E2><E3><CF><D3>
4 0 obj 
<<
/Filter /FlateDecode
/Length 23
>>
stream
x<9C>+T0<D0>3T0^@A(<9D><9C><CB>^U<C8>^E^@5I^De
endstream 
endobj 
3 0 obj 
<<
/Resources 
<<
/ProcSet [/PDF]
>>
/Type /Page
/Parent 1 0 R
/Contents 4 0 R
/MediaBox [0 0 595 842]
>>
endobj 
7 0 obj 
<<
/Filter /FlateDecode
/Length 23
>>
stream
x<9C>+T0<D0>3T0^@A(<9D><9C><CB>^U<C8>^E^@5I^De
endstream 
endobj 
6 0 obj 
<<
/Resources 
<<
/ProcSet [/PDF]
>>
/Type /Page
/Parent 1 0 R
/Contents 7 0 R
/MediaBox [0 0 595 842]
>>
endobj 
1 0 obj 
<<
/Kids [3 0 R 6 0 R]
/Type /Pages
/Count 2
>>
endobj 
9 0 obj 
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj 
10 0 obj 
<<
/ModDate (D:20160103144953+05'30')
/CreationDate (D:20160103144953+05'30')
/Creator (pdftk 2.02 - www.pdftk.com)
/Producer (itext-paulo-155 \(itextpdf.sf.net-lowagie.com\))
>>
endobj xref
0 11
0000000000 65535 f 
0000000455 00000 n 
0000000000 65535 f 
0000000112 00000 n 
0000000015 00000 n 
0000000000 65535 f 
0000000332 00000 n 
0000000235 00000 n 
0000000000 65535 f 
0000000520 00000 n 
0000000571 00000 n 
trailer

<<
/Info 10 0 R
/ID [<cd7858cb595d5fbe3dd38c1258972091><390fa471e3236a790d9231e931a73695>]
/Root 9 0 R
/Size 11
>>
startxref
767
%%EOF
Run Code Online (Sandbox Code Playgroud)

谁能解释为什么这样做有问题?我认为sh(或者bash,它没有区别)只会忽略该行并继续前进。

另外,我可以做一些类似的事情吗?当我使用 时pdftk,我会向它传递一个文件列表,其中一些我可能想不时评论或取消评论。

pdftk在 Debian 8.2 (jessie) 上使用2.02-2。


对于那些想知道行为的人来说,最小的复制器:

$ cat test.sh
#!/bin/sh
echo \
    A \
#   B \
    C
$ ./test.sh
A
./test.sh: line 5: C: command not found
Run Code Online (Sandbox Code Playgroud)

Moh*_*mad 8

原因

首先,让我们看一下 bash 的手册页:

未加引号的反斜杠 () 是转义字符。它保留了下一个字符的字面值,除了 . 如果出现 \<newline> 对,并且反斜杠本身没有被引用,则 \<newline> 被视为行继续(即,它从输入流中删除并被有效地忽略)。

因此,当您\在 a 之前使用时<newline>,它会将其视为行的延续。但是在您的第二个脚本中,您的代码的延续 afterA=blank.pdf是注释,并且因为\afterB=blank.pdf也是您的注释的一部分,所以它不会导致行继续。因此,脚本的其余部分将被视为另一个独特的命令。所以 bash 会认为你的脚本是这样的:

#!/bin/sh

echo "" | ps2pdf -sPAPERSIZE=a4 - blank.pdf

pdftk A=blank.pdf #   B=blank.pdf \
C=blank.pdf cat A C output foo.pdf
Run Code Online (Sandbox Code Playgroud)

在这里,您很可能会收到类似C=blank.pdf: command not found.

反引号来拯救!

但是,您可以通过使用反引号的命令替换功能,在行继续中使用注释,如下所示:

#!/bin/sh

echo "" | ps2pdf -sPAPERSIZE=a4 - blank.pdf

pdftk \
    A=blank.pdf \
`#  B=blank.pdf` `#You added left-side comment and I added this comment and everything is awesome` \
    C=blank.pdf \
    cat A C \
    output foo.pdf
Run Code Online (Sandbox Code Playgroud)

这不是我的聪明才智。它是Marwan Alsabbagh的。有关更多详细信息,请参阅以下链接:

如何为多行命令添加行注释