在 Ubuntu 16.04.3 中,我有一个非常简单的 bash 脚本:
测试文件
[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0
当我以非 root 用户me
或 身份调用它时root
,它会按预期工作。如果我使用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
可以正常使用此脚本以及sudo
?
答案1
每个脚本都以舍邦,如果没有它,启动脚本的 shell 就不知道哪个解释器应该运行脚本1,并且可能会(就像sudo ./script.sh
这里的情况一样)使用 运行它sh
,在 Ubuntu 16.04 中它链接到dash
。条件表达式 [[
是bash
复合命令,所以dash
不知道如何处理它并抛出您遇到的错误。
这里的解决方案是添加
#!/bin/bash
作为脚本的第一行。使用 显式调用时可能会得到相同的结果sudo bash ./script.sh
,但 shebang 才是最佳选择。
要检查哪个 shell 运行您的脚本,请将其添加echo $0
到其中。这就是不一样 echo $SHELL
,引用wiki.archlinux.org:
SHELL 包含用户首选 shell 的路径。请注意,这不一定是当前正在运行的 shell,尽管 Bash 在启动时设置了此变量。
1:正如您一开始./test.sh
所bash
假设的bash
,子壳层也是如此sudo su
。
答案2
作为@dessert 解释,这里的问题是你的脚本没有舍邦线。如果没有 shebang,sudo
将默认尝试使用 运行文件/bin/sh
。我在任何地方都找不到它的文档,但我通过检查sudo
源代码确认了这一点,我在文件中找到以下内容pathnames.h
:
#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif /* _PATH_BSHELL */
这意味着“如果变量_PATH_BSHELL
未定义,则将其设置为/bin/sh
”。然后,在configure
源 tarball 中包含的脚本中,我们有:
for p in "/bin/bash" "/usr/bin/sh" "/sbin/sh" "/usr/sbin/sh" "/bin/ksh" "/usr/bin/ksh" "/bin/bash" "/usr/bin/bash"; do
if test -f "$p"; then
found=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $p" >&5
$as_echo "$p" >&6; }
cat >>confdefs.h <<EOF
#define _PATH_BSHELL "$p"
EOF
break
fi
done
此循环将查找/bin/bash
、/usr/bin/sh
、/sbin/sh
或,/usr/sbin/sh
然后/bin/ksh
将设置_PATH_BSHELL
为以先发现者为准。由于/bin/sh
是列表中的第一个,并且它存在,因此_PATH_BSHELL
设置为/bin/sh
。所有这些的结果是,sudo
除非另有定义,否则 的默认 shell 是/bin/sh
。
因此,sudo
将默认使用/bin/sh
和 来运行程序,在 Ubuntu 上,这是一个符号链接dash
,即一个最小的 POSIX 兼容 shell:
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 27 2015 /bin/sh -> dash
该[[
构造是 bash 的一个特性,它不是由 POSIX 标准定义的,也不能被以下程序理解dash
:
$ bash -c '[[ true ]] && echo yes'
yes
$ dash -c '[[ true ]] && echo yes'
dash: 1: [[: not found
具体来说,在您尝试的三次调用中:
./test.sh
否
sudo
;如果没有 shebang 行,您的 shell 将尝试执行文件本身。由于您正在运行bash
,因此这将有效运行bash ./test.sh
并起作用。sudo su
其次是./test.sh
。在这里,您将为用户启动一个新的 shell
root
。这将是$SHELL
该用户在环境变量中定义的任何 shell,在 Ubuntu 上,root 的默认 shell 是bash
:$ grep root /etc/passwd root:x:0:0:root:/root:/bin/bash
sudo ./test.sh
在这里,您直接让其
sudo
执行命令。由于其默认 shell/bin/sh
如上所述,这会导致它使用 运行脚本/bin/sh
,即 ,dash
并且由于dash
不理解而失败[[
。
笔记:如何设置默认 shell 的细节sudo
似乎有点复杂。我尝试将答案中提到的文件更改为指向,/bin/bash
但sudo
仍然默认为/bin/sh
。因此,源代码中一定有其他地方定义了默认 shell。尽管如此,主要观点(sudo
默认为sh
)仍然成立。