在 Ubuntu 16.04.3 中,我有一个非常简单的 bash 脚本:
测试.sh
[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0
当我将它称为非 root 用户me
或 asroot
时,它按预期工作。如果我使用sudo ./test.sh
,它会抱怨语法错误:
$ ./test.sh
true
me /bin/bash ./test.sh
$ sudo su
# ./test.sh
true
root /bin/bash ./test.sh
# exit
$ sudo ./test.sh
./test.sh: 1: ./test.sh: [[: not found
false
root /bin/bash ./test.sh
这可能是什么原因造成的?我怎样才能修复它,以便me
可以正常使用这个脚本和 with sudo
?
每个脚本都以Shebang开头,没有它,启动脚本的 shell 不知道应该由哪个解释器运行您的脚本1并且可能 - 就像
sudo ./script.sh
这里的情况一样 - 运行它sh
,在 Ubuntu 16.04 中链接到dash
. 条件表达式[[
是一个bash
复合命令,所以dash
不知道如何处理并抛出你遇到的错误。这里的解决方案是添加
作为脚本的第一行。当您使用 明确调用它时,您可能会得到相同的结果
sudo bash ./script.sh
,但是 shebang 是要走的路。要检查哪个 shell 运行您的脚本,请添加
echo $0
到它。这与echo $SHELL
引用wiki.archlinux.org的情况不同:1:正如您刚开始假设
./test.sh
的那样,子shell也是如此。bash
bash
sudo su
正如@dessert 解释的那样,这里的问题是您的脚本没有shebang 行。如果没有 shebang,
sudo
将默认尝试使用/bin/sh
. 我在任何地方都找不到它的文档,但我通过检查在sudo
文件中找到以下内容的源代码进行了确认pathnames.h
:这意味着“如果
_PATH_BSHELL
未定义变量,请将其设置为/bin/sh
”。然后,在configure
源 tarball 中包含的脚本中,我们有:此循环将查找
/bin/bash
、/usr/bin/sh
、或/sbin/sh
,然后将 设置为首先找到的那个。由于是列表中的第一个并且它存在,因此设置为. 所有这一切的结果是,除非另有定义,否则的默认 shell是./usr/sbin/sh
/bin/ksh
_PATH_BSHELL
/bin/sh
_PATH_BSHELL
/bin/sh
sudo
/bin/sh
因此,
sudo
将默认使用/bin/sh
和在 Ubuntu 上运行东西,这是一个符号链接dash
,一个最小的 POSIX 兼容 shell:该
[[
构造是一个 bash 功能,它不是由 POSIX 标准定义的,也不被理解dash
:详细地说,在您尝试的三个调用中:
./test.sh
没有
sudo
;在没有 shebang 行的情况下,您的 shell 将尝试执行文件本身。由于您正在运行bash
,这将有效地运行bash ./test.sh
和工作。sudo su
其次是./test.sh
。在这里,您正在为 user 启动一个新的 shell
root
。这将是在$SHELL
环境变量中为该用户定义的任何 shell,在 Ubuntu 上,root 的默认 shell 是bash
:sudo ./test.sh
在这里,您让
sudo
直接执行命令。由于它的默认 shell/bin/sh
如上所述,这会导致它使用 运行脚本/bin/sh
,这是dash
因为dash
不理解而失败[[
。注意:如何设置默认 shell 的细节
sudo
似乎有点复杂。我尝试将答案中提到的文件更改为指向/bin/bash
但sudo
仍默认为/bin/sh
. 所以源代码中肯定还有其他一些地方定义了默认的shell。尽管如此,主要观点(sudo
默认为sh
)仍然存在。