bash 脚本:使用或不使用 sudo 调用时会产生不同的结果

bash 脚本:使用或不使用 sudo 调用时会产生不同的结果

在 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.shbash假设的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

具体来说,在您尝试的三次调用中:

  1. ./test.sh

    sudo;如果没有 shebang 行,您的 shell 将尝试执行文件本身。由于您正在运行bash,因此这将有效运行bash ./test.sh并起作用。

  2. sudo su其次是./test.sh

    在这里,您将为用户启动一个新的 shell root。这将是$SHELL该用户在环境变量中定义的任何 shell,在 Ubuntu 上,root 的默认 shell 是bash

    $ grep root /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    
  3. sudo ./test.sh

    在这里,您直接让其sudo执行命令。由于其默认 shell/bin/sh如上所述,这会导致它使用 运行脚本/bin/sh,即 ,dash并且由于dash不理解而失败[[


笔记:如何设置默认 shell 的细节sudo似乎有点复杂。我尝试将答案中提到的文件更改为指向,/bin/bashsudo仍然默认为/bin/sh。因此,源代码中一定有其他地方定义了默认 shell。尽管如此,主要观点(sudo默认为sh)仍然成立。

相关内容