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

Jam*_*ton 10 bash sudo scripts

在 Ubuntu 16.04.3 中,我有一个非常简单的 bash 脚本:

测试文件

[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0
Run Code Online (Sandbox Code Playgroud)

当我将它称为非 root 用户me或 as 时root,它按预期工作。如果我使用sudo ./test.sh,它会抱怨语法错误:

[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0
Run Code Online (Sandbox Code Playgroud)

什么可能导致这种情况?如何修复它以便me可以正常使用此脚本并与 一起使用sudo

des*_*ert 20

每个脚本都以Shebang开头,没有它,启动脚本的 shell 不知道哪个解释器应该运行您的脚本1并且可能 - 就像sudo ./script.sh这里的情况- 运行它sh,在 Ubuntu 16.04 中链接到dash. 该条件表达式 [[是一个bash复合命令,所以dash不知道如何处理它,并抛出你遇到的错误。

这里的解决方案是添加

#!/bin/bash
Run Code Online (Sandbox Code Playgroud)

作为脚本的第一行。当您使用 显式调用它时,您可能会得到相同的结果sudo bash ./script.sh,但使用 shebang 是可行的方法。
要检查哪个 shell 运行您的脚本,请添加echo $0到它。这 echo $SHELL引用wiki.archlinux.org 不同

SHELL 包含用户首选 shell 的路径。请注意,这不一定是当前正在运行的 shell,尽管 Bash 在启动时设置了这个变量。

1:当你开始./test.sh使用bash它只是假设bash,也是如此的sudo su子shell。


ter*_*don 5

正如@dessert 解释的那样,这里的问题是您的脚本没有shebang 行。如果没有shebang,sudo将默认尝试使用/bin/sh. 我在任何地方都找不到它的记录,但我通过检查sudo源代码确认了我在文件中发现以下内容pathnames.h

#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif /* _PATH_BSHELL */
Run Code Online (Sandbox Code Playgroud)

这意味着“如果变量_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
Run Code Online (Sandbox Code Playgroud)

这个循环会寻找/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
Run Code Online (Sandbox Code Playgroud)

[[构造是 bash 功能,它不是由 POSIX 标准定义的,并且不被以下人员理解dash

$ bash -c '[[ true ]] && echo yes'
yes
$ dash -c '[[ true ]] && echo yes'
dash: 1: [[: not found
Run Code Online (Sandbox Code Playgroud)

详细地说,在您尝试的三个调用中:

  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
    
    Run Code Online (Sandbox Code Playgroud)
  3. sudo ./test.sh

    在这里,您让sudo直接执行命令。由于它的默认 shell/bin/sh如上所述,这会导致它运行带有 的脚本/bin/shdash它失败了,因为dash不理解[[.


注意:如何sudo设置默认 shell的细节似乎有点复杂。我尝试将答案中提到的文件更改为指向,/bin/bashsudo仍然默认为/bin/sh. 所以在源代码中肯定还有一些其他地方定义了默认的shell。尽管如此,要点(sudo默认为sh)仍然有效。