我正在编写一个需要根级权限的脚本,我想让它这样做,如果脚本不是以根身份运行,它只是回显“请以根身份运行”并退出。

下面是我正在寻找的一些伪代码:

if (whoami != root)
  then echo "Please run as root"

  else (do stuff)
fi

exit

我怎样才能最好地(干净和安全地)完成这个任务呢?谢谢!

啊,只是澄清一下:(做东西)部分将涉及运行本身需要root的命令。因此,作为一个普通用户运行它只会出现一个错误。这只是为了干净地运行一个需要根命令的脚本,而不需要在脚本中使用sudo,我只是在寻找一些语法糖。


当前回答

正如@ wrkken在他的评论中提到的,id -u是一个更好的根检查。

此外,通过正确使用sudo,您可以检查脚本,看看它是否以根用户身份运行。如果不是,让它通过sudo收回自己,然后以根权限运行。

根据脚本的功能,另一个选项可能是为脚本可能需要的任何专门命令设置一个sudo条目。

其他回答

我无意中发现了同样的问题。行#!/bin/su root将是一个优雅的方法,但在某些(高级)bash版本上不起作用,就像@basickarl提到的那样。此外,当您使用/bin/bash ./some_script.(b)sh启动脚本时,此方法可能会失败。

我找到了另一个关于SO的页面,其中使用了环境变量SUDO_UID和SUDO_USER。当一个普通用户使用sudo调用一个脚本时,这些在脚本中是可用的。否则它们就是空的。另外:当你以根用户身份登录时,这些值也是空的(或未设置-不完全确定)。

所以这是我目前使用的解决方案:

isSudo()
{
  local _fn="${FUNCNAME[0]}"
  if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
    if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
      >2 echo -e "$_fn: invoked as sudo-user ($SUDO_USER)."
    else
      >2 echo -e "$_fn: invoked as root ($SUDO_USER)."
    fi
    return 0
  fi
  >2 echo "$_fn: not as root or as sudoer."
  return 1
}

然后像这样使用它。

if $(isSudo) ; then
  echo -e "Script started as root or as sudoer."
else
  echo -e "Script not started as root or with sudo"
fi

在echo前面的>2是为了将echo重定向到stderr,否则if语句就会失控,这只是为了进行一点调试。你可以在局部变量中保存“root”、“sudoer”或“user”,然后让函数将其回显,然后执行一个case,就像这样:

how_invoked()
{
  local _invoked_as="USER"
  if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
    if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
      _invoked_as="SUDOER"
    else
      _invoked_as="ROOT"
    fi
  fi
  echo -n "$_invoked_as" # -n prevents newline
}

然后像这样使用它:

INVOKED_AS=$(how_invoked)
case $INVOKED_AS in
  USER)
    echo -e "Invoked as user." ;;
  SUDOER)
    echo -e "Invoked as sudoer." ;;
  ROOT)
    echo -e "Invoked as root." ;;
  *)
    echo -e "I'm confused" ;;
esac

或者如果你只是想测试其中一个:

if [ "$(how_invoked)" == "SUDOER" ] ; then
  echo -e "You are a sudoer."
else
  echo -e "You are NOT a sudoer."
fi

希望这能有所帮助。

如果脚本确实需要根访问权限,那么它的文件权限应该反映这一点。非根用户可执行根脚本将是一个危险信号。我建议您不要使用if检查来控制访问。

chown root:root script.sh
chmod u=rwx,go=r script.sh

试试下面的代码:

#!/bin/sh

if [ "$(id -u)" -ne 0 ]; then
    echo "Sorry, you are not root." >&2
    exit 1
fi

编辑时注意:反撇号现在被认为已弃用(还有很多注意事项)。

有一个简单的检查用户是否为root。

# Fancy red-colored `error` function with `stderr` redirection with `exit`.
error ()
{
    { printf '\E[31m'; echo "$@"; printf '\E[0m'; } >&2
    exit 1
}

# Test for root user.
if [ "$EUID" -eq 0 ]; then
    error "Do not run this as the root user"
fi

这还假设如果失败,您希望以1退出。error函数是一种将输出文本设置为红色的功能(不需要,但如果你问我的话,我觉得这很不错)。

在这个回答中,我假定读者能够理解bash、zsh等现代shell与dash等便携式POSIX shell之间的区别。

我相信这里没有太多要解释的,因为高投票的答案已经很好地解释了大部分问题。

但是,如果有什么需要进一步解释的,请不要犹豫,我会尽我最大的努力填补空白。


优化的性能和可靠性全方位解决方案;所有外壳兼容

新的解决方案:

# bool function to test if the user is root or not
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }

基准测试(保存到文件is_user_root__benchmark)

#+------------------------------------------------------------------------------+
#|                           is_user_root() benchmark                           |
#|                  "Bash is fast while Dash is slow in this"                   |
#|                          Language: POSIX shell script                        |
#|                     Copyright: 2020-2021 Vlastimil Burian                    |
#|                      M@il: info[..]vlastimilburian[..]cz                     |
#|                               License: GPL 3.0                               |
#|                                 Version: 1.2                                 |
#+------------------------------------------------------------------------------+

readonly iterations=10000

# intentionally, the file does not have an executable bit, nor it has a shebang
# to use it, just call the file directly with your shell interpreter like:

# bash is_user_root__benchmark    ## should take a fraction of one second
# dash is_user_root__benchmark    ## could take around 10 seconds

is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }

print_time   () { date +"%T.%2N"; }
print_start  () { printf '%s' 'Start  : '; print_time; }
print_finish () { printf '%s' 'Finish : '; print_time; }

printf '%s\n' '___is_user_root()___'; print_start
                   
i=1; while [ "$i" -lt "$iterations" ]; do
    is_user_root
    i=$((i+1))
done; print_finish

使用和持续时间的例子:

$ dash is_user_root__benchmark 
___is_user_root()___
Start  : 03:14:04.81
Finish : 03:14:13.29

$ bash is_user_root__benchmark 
___is_user_root()___
Start  : 03:16:22.90
Finish : 03:16:23.08

解释

由于读取$EUID标准bash变量(有效的用户ID号)要比执行ID -u命令以POSIX-ly方式查找用户ID快许多倍,因此该解决方案将两者结合到一个包装良好的函数中。当且仅当$EUID由于某种原因不可用时,将执行id -u命令,以确保在任何情况下都能获得正确的返回值。


为什么我在OP问了这么多年之后才发布这个解决方案

好吧,如果我没看错的话,上面似乎确实少了一段代码。

你看,有许多变量必须考虑在内,其中之一是结合性能和可靠性。


便携式纯POSIX解决方案+上述函数的使用示例

#!/bin/sh

# bool function to test if the user is root or not (POSIX only)
is_user_root ()
{
    [ "$(id -u)" -eq 0 ]
}

if is_user_root; then
    echo 'You are the almighty root!'
    # You can do whatever you need...
else
    echo 'You are just an ordinary user.' >&2
    exit 1
fi

结论

尽管您可能不喜欢它,但Unix / Linux环境已经变得非常多样化。这意味着有些人非常喜欢bash、zsh和其他现代shell,他们甚至不会考虑可移植性(POSIX)。如今,这是个人选择和需要的问题。