我正在将嵌入式平台上的 bash 从 4.1.9 更新到最新版本 (4.4.12),并且我看到在将转义参数传递到脚本的这个简单场景中行为发生了变化。
脚本/tmp/printarg:
#! /bin/sh
echo "ARG |$*|"
我像这样调用脚本:
bash -c "/tmp/printarg \\"abc\\""
我已经在运行 bash 4.3.42 的多个平台(本机 x86_64 Linux)以及运行 bash 4.1.9 和 4.2.37 的多个嵌入式平台(ARM 和 PPC)上尝试过此操作,所有这些平台都报告了我所期望的结果:
38$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc|
但是,当我使用 bash 4.4.12(本机 X86 或嵌入式平台)运行此命令时,我得到以下信息:
$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc\| <<< trailing backslash
如果我在命令行中的第二个转义引号和结尾引号之间添加一个空格,那么我将不再看到额外的反斜杠:
$ bash -c "/tmp/printarg \\"abc\\" "
ARG |abc | <<< trailing space, but backslash is gone
这感觉就像是一种回归。有什么想法吗?我还尝试通过更改启用各种 compat 选项(compat40、compat41、compat42、compat43)。
答案1
bash -c "/tmp/printargs \\"abc\\""
不会逃避你所想的。反斜杠-反斜杠是转义的反斜杠,由调用 shell 处理——因此与运行相同:
/tmp/printargs \abc\
因为双引号是不是逃脱了。你可以直接写:
bash -c '/tmp/printargs \abc\'
我猜你实际上想要:
bash -c "/tmp/printargs \"abc\""
它转义双引号,将带引号的“abc”传递给 bash -c。
(我猜你看到的不同行为是不同版本的 bash 在输入结束时以不同方式处理转义的任何内容。)
Perl 版本的 printargs(行为略有改进):
#!/usr/bin/perl
use feature qw(say);
for (my $i = 0; $i < @ARGV; ++$i) {
say "$i: |$ARGV[$i]|";
}
答案2
bash -c "/tmp/printargs \\"abc\\""
您确定这是您想要做的吗?如果您set -x
有效地运行该命令,您将看到运行的命令是
bash -c '/tmp/printargs \abc\'
即,您向 shell 传递一个以反斜杠结尾的字符串。第一个带引号的字符串包含一个转义的反斜杠,然后是一个不带引号的abc
、一个转义的反斜杠,然后是一个空的带引号的字符串。 (请注意 Stackexchange 完成的语法突出显示如何显示abc
未引用。)
输入末尾不带引号的反斜杠没有多大意义。反斜杠可以转义后面的字符,或者开始一个连续行,它与下面的换行符一起被删除,如下所示:
$ bash -c $'echo "foo\\\nbar"'
foobar
本案两者都没有。您可能正在尝试执行以下任一操作:
bash -c "/tmp/printargs \"abc\""
bash -c '/tmp/printargs "abc"'
两者都会产生输出ARG |abc|
。
我们可以通过更简单的测试来看到 shell 之间的差异:
$ bash -c 'echo $BASH_VERSION; echo abc\'
4.4.12(1)-release
abc\
$ ./bash -c 'echo $BASH_VERSION; echo abc\'
4.3.30(1)-release
abc
$ dpkg -l dash |grep ^i
ii dash 0.5.8-2.4 amd64 POSIX-compliant shell
$ dash -c 'echo abc\'
abc\
$ dpkg -l zsh |grep ^i
ii zsh 5.3.1-4+b2 amd64 shell with lots of features
$ zsh -c 'echo abc\'
abc
如果我必须猜测,我会开始在这个变化中寻找变化的根源:
本文档详细介绍了该版本 bash-4.4-alpha 和 bash-4.4-alpha 之间的变化 以前的版本,bash-4.3-release。 1.Bash 的更改 cccc。修复了读取时导致评估短路的错误 来自以不带引号的反斜杠结尾的字符串的命令,或者在采购时 以不带引号的反斜杠结尾的文件。
答案3
我来解释一下这个:
$ bash -c "/tmp/quotefail \\"abc\\" "
ARG |abc | <<< trailing space, but backquote is gone
正如 @ilkkachu 所解释的,set -x
我们看看这是如何解释的:
+ bash -c '/tmp/quotefail \abc\ '
当然,“\a”就是“a”,“\”就是“”,所以 /tmp/quotefail 接收到的参数是“abc”,结果:
ARG |abc |
在第一个测试中,反斜杠后面没有跟任何东西,所以它仍然是反斜杠。