我写了一个使用位置参数的“内联脚本”,例如
$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
我实际上想使用sudo --login
. 但是位置参数不起作用。有没有办法让它们工作?
(如果行为在某处被记录或标准化,我也很感兴趣)。
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2
软件版本:
$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
出于同样的原因
不输出
$HOME
,而是$HOME
变量的内容(目标用户的,由 设置sudo
)。在您的情况下,这不是因为sh -c 没有扩展位置参数,而是因为
$0
,$1
,在启动$2
时已经(通过登录外壳)扩展了sh
。这里更令人惊讶的是为什么你会看到
all=1 2
.$@
这与您看到的原因相同sudo --login echo '$@'
。-s
/--shell
和-i
/都--login
运行一个 shell 来运行命令。但sudo
它以一种非常奇怪的方式进行。我希望
sudo -s 'some shell code'
运行一个 shell 来解释some shell code
,但这不是它的作用。使用-s
,它仍然希望运行一个简单的命令,并尝试引用(使用\
)一些字符,以期通过该 shell 实现它。你不是故意的
sudo -s 'echo test'
,但是sudo -s echo test
。在第一种情况下,sudo 实际上是echo\ test
作为 shell 代码传递的。在类似 Bourne 的 shell 中,它会尝试运行一个名为'echo test'
. 使用rc
shell,它将运行一个echo\
以test
作为参数调用的命令。除了 SPC 之外,还有一些字符会
sudo
转义。这包括反引号,;
,|
,(
,)
,*
,&
,=
,\
但奇怪的是,不是(或者这可能是这些/选项$
中唯一存在的理由:让 shell 扩展变量)。-s
--login
它确实包括
@
。如果您查看代码,它会转义除 ASCII alnum、 和 之外的_
所有-
字节$
。所以:
sudo
实际运行"$SHELL" -c 'echo $\@'
,这解释了为什么在您的示例中,$@
不是由启动的 shellsudo --login
而是由您告诉它运行的 shell 扩展的。在你的
sudo
运行root
的登录外壳(bash
在您的情况下)并告诉它解释:参数已用空格和所有 SPC、
"
、=
、&
、@
字符连接,但没有$
被转义。因为那些$
没有被转义的 bash 登录 shell 会扩展$0
,$1
,$2
但不是$\@
因为那个反斜杠。你可以看到它:
所以登录
bash
shell最终sh
以-c
第一个参数运行,作为第二个参数,
sh
,1
,2
作为第 3、第 4 和第 5 个。要解决它,您可以使用
${0}
而不是$0
, 因为sudo
将其转换为$\{0\}
可防止登录 shell 将其扩展到其内容$0
:编辑:历史揭示了其背后的原因
查看代码时的更多发现。
$
显然是在 2013 年通过这种变化引入了non-escaping 。其中指的是bug#564指的是bug#413。看起来在那个 bug#413 被"fixed"之前,
sudo
表现得和我预期的一样。那是:
让登录 shell 解释该 shell 代码(并
sudo -s 'some shell code'
解释$SHELL
该 shell 代码)。但是 bug#413 解决方案破坏了它,因为报告错误的人不理解它是这样工作的。并且错误#564 进一步破坏了它(试图仅恢复错误#413 解决方案引入的部分破坏)。我从 2009 年开始编译
sudo
1.7.1 并且-s
/-i
选项按我预期的那样工作。1.7.3
(具有 bug#413 分辨率)的工作方式与您显然预期的一样:在修复该错误#413 时引入的转义很容易被愚弄,因为
\
在所有字节(而不是字符)前面敲击 a 并不适用于所有人。除了
rc
where\
甚至不是引用运算符的明显情况之外,在大多数 shell 中,换行符不能用 引用\
:这也意味着空参数被丢弃:
\
此外,通过在每个byte之前插入 a ,它将字符转换为字符集中的其他字符,其中\
在其他字符中找到字节 0x5c (的编码):0xa3 0x60 是该语言环境中的希腊 Epsilon。sudo 将 0x60s 更改为 0x5c 0x60 和 0xa3 0x5c 是希腊字母,这就解释了为什么
uname
运行命令。sudo
改变了至
当然,行为最终令人惊讶,因为
$-
,$$
, 位置参数和$varname
(其中varname
是有效的 POSIX 变量名)被扩展,但其他参数(如$@
这里,还有$!
,$?
,$*
,$#
)或${varname}
or$var[1]
(csh
/tcsh
/zsh
) 或$var(1)
(rc
/es
/...)。