使用sudo -s
(“--shell”选项的缩写)时,可以传递“sudo”命令,在这种情况下,它将在由“sudo”作为目标用户启动的 shell 中运行命令。
(同样,sudo -i
也可作为“--login”选项使用,也可以启动一个 shell 并类似地接受一个命令,其行为方式相同。)
在许多情况下,通过 shell 在 sudo 中运行命令可能很重要:
- 在当前用户无权访问但 root 可以访问的目录下使用通配符时,该命令需要在 root shell 下运行才能正确扩展通配符。
- 运行整个管道,许多命令链接在管道 (
|
) 中。 - 在运行 shell 内置程序时,例如
for
,if
等。在单个下运行一个小的整个内联“脚本”sudo
可能很有用。
“-s”选项的文档说(强调我的):
运行由
SHELL
环境变量指定的 shell(如果已设置)或由调用用户的密码数据库条目指定的 shell。如果指定了命令,则通过 shell 的-c选项将其传递给 shell 以执行。如果未指定命令,则执行交互式 shell。请注意,与交互式会话相比,当指定命令时,大多数 shell 的行为都不同;有关详细信息,请参阅 shell 手册。
换句话说,当传递sudo -s
命令时,它使用-c
选项传递给shell,该选项接受一个带有“脚本”的字符串,然后继续将其作为shell脚本执行。
该文档实际上并没有进一步说明如何使用此选项或提供示例,只是说“请参阅 shell 手册以获取详细信息”。这意味着收到的命令直接传递给 shell 的-c
选项。然而,事实证明,事实并非如此。
向它传递一个包含多个单词的 shell 脚本失败:
$ sudo -s 'ls -ld /var/empty'
/bin/bash: ls -ld /var/empty: No such file or directory
错误消息暗示它试图将整个字符串作为一个简单的命令运行......嗯,好的,所以也许添加空格会起作用?是的,情况就是这样:
$ sudo -s ls -ld /var/empty
drwxr-xr-x. 3 root root 18 Jul 12 21:48 /var/empty
不过,这并不是 shell 的真正-c
工作方式……哦,好吧,让我们尝试使用一些元字符,例如~
,它是主目录的快捷方式,来看看它的行为方式。注意~
需要引用,以防止非 sudo shell 扩展它(在这种情况下,它将扩展到非 root 用户的家,而不是/root
预期的):
$ sudo -s ls '~'
ls: cannot access '~': No such file or directory
好的,所以这不起作用,错误输出似乎暗示扩展没有发生,因为它在~
那里保留了一个文字。
通配符呢?也不工作:
$ sudo -s ls '/root/*.cfg'
ls: cannot access '/root/*.cfg': No such file or directory
在这两种情况下,运行命令都可以$SHELL -c
正常工作。在这种情况下,$SHELL
是 bash,所以:
$ sudo bash -c 'ls ~'
anaconda-ks.cfg
$ sudo bash -c 'ls /root/*.cfg'
/root/anaconda-ks.cfg
一个例外是变量似乎对 起作用sudo -s
,例如:
$ sudo -s echo '$HOME'
/root
所以:
- 这里发生了什么?
- 为什么通配符和元字符(例如)在传递给or
~
的命令上不起作用?sudo -s
sudo -i
- Given
$SHELL -c
需要一个带有脚本的字符串,但sudo -s
需要多个参数,脚本是如何从参数中组装起来的? - 在 shell 上运行命令的可靠方法是什么
sudo
?
TL;DR:在“--shell”或“--login”选项上执行命令时,
sudo
将转义大多数字符(使用反斜杠转义),包括所有元字符(除了$
),还包括空格。这打破了您想要使用 shell 的每个用例,这使得
sudo -s
大多数情况下不适合运行需要作为 shell 命令运行的 shell 命令。代替
sudo -s
, 使用sudo sh -c '...'
orsudo bash -c '...'
(或任何预期$SHELL
的东西。)代替sudo -i
, 使用sudo bash -l -c '...'
代替(假设 root 的 shell 再次是 bash 。)查看sudo 源代码的相关部分,有这个片段:
该片段运行在每个参数的每个字符上。如果字符不是字母数字、下划线、破折号或美元符号,它将使用反斜杠进行转义。
这意味着通配符
*
,?
,[...]
等将转义为\*
,\?
,\[...\]
。诸如
~
,;
,|
等元字符也将转义为\~
,\;
,\|
。带有空格的字符串
ls /root
将转义为ls\ /root
.出于某种原因,
$
转义是一个例外,这就是为什么sudo -s echo '$HOME'
有效,因为$
将保持未转义。(请注意,这仅适用于变量,甚至不适用于${HOME}
形式,在这种情况下,花括号将被转义,这将破坏表达式。)这种引用的后果是,大多数需要以 root 身份运行 shell 的情况并不能真正从以下
sudo -s <command>
格式中受益:|
将被转义并且也不起作用。for
并且if
通常需要使用;
的命令将被转义并且也不起作用。换行符将是另一种选择,但似乎也没有办法引入未转义的换行符。简而言之,这种形式
sudo -s <command>
似乎只适用于不涉及通配符、管道、scriptlet 等的情况。但是,您通常并不需要 shell 来运行这些命令!sudo <command>
对于这些情况,仅在没有的情况下运行-s
通常就足够了。目前尚不清楚 sudo 实施的动机是什么。也许是为了在添加或删除
-s
. 也许也是因为-i
之前-s
,在这种情况下,环境变量的设置方式存在一些细微差别......变通方法:变通方法是
-c
显式运行 shell。这涉及了解
$SHELL
目标用户的内容(可能与当前用户不匹配,因此$SHELL
直接使用并不总是正确的。)例如,代替
sudo -s ls -l '/root/*.cfg'
,使用:而不是
sudo -i ls -l '~'
, 使用:(bash 的
-l
参数创建了一个“登录”shell,它等同于由 . 创建的那个sudo -i
。)