如何检查是不是在 bash 脚本中以 root 身份运行

Posted

技术标签:

【中文标题】如何检查是不是在 bash 脚本中以 root 身份运行【英文标题】:How to check if running as root in a bash script如何检查是否在 bash 脚本中以 root 身份运行 【发布时间】:2013-08-15 11:24:30 【问题描述】:

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

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

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

  else (do stuff)
fi

exit

我怎样才能最好地(干净而安全地)实现这一目标?谢谢!

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

【问题讨论】:

(1) 使其不能被其他任何人执行,然后 root (2) 也安排进一步的权限.. (3) id -u 为 root 返回 0 【参考方案1】:

请务必注意,每当您使用 sudo 运行脚本时,“用户上下文”或环境都会切换到 root

但是 Teo 是什么意思?

好吧,我的年轻学徒,这意味着如果 padawan 用户使用 sudo 运行包含波浪号 (~) 的脚本,那么每当 bash 扩展 ~ 时,结果将是 /root而不是/home/<user>(即在本例中为/home/padawan),或者如果您创建目录或文件,则所有者和组将是root,而不是在本例中执行脚本的padawan,因为用户环境已切换。

例如,让我们检查一下这个脚本install-app.sh

#!/bin/bash
ROOT_UID=0   # Only users with $UID 0 have root privileges.
E_NOTROOT=87 # Non-root exit error.

## Prevent the execution of the script if the user has no root privileges
if [ "$UID:-$(id -u)" -ne "$ROOT_UID" ]; then
    echo 'Error: root privileges are needed to run this script'
    exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
service mysql start
...

如果我们使用sudo运行:

sudo install-app.sh

这将创建目录,config 文件将如下所示:

##
## ~ (/root)
drwxr-xr-x 17 root root    4096 Nov 23 20:45 ./
drwxr-xr-x  5 root root    4096 Nov 15 19:04 ../
...
drwxr-xr-x  3 root root    4096 Nov 25 14:30 app/
...
drwxr-xr-x  2 root root    4096 Nov 16 19:08 tmp/

## ~/app (/root/app)
drwxr-xr-x  3 root root 4096 Nov 25 14:30 ./
drwxr-xr-x 17 root root 4096 Nov 25 14:33 ../
drwxr-xr-x  2 root root 4096 Nov 25 14:33 init/

## ~/app/init (/root/app/init)
drwxr-xr-x 2 root root 4096 Nov 25 14:33 ./
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root    0 Nov 25 14:33 config

## /home/<user>/app/conf
drwxr-xr-x 2 <user> <user> 4096 Nov 25 14:43 ./
drwxr-xr-x 3 <user> <user> 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root   root      0 Nov 25 14:43 profile

如你所知,她的剧本一团糟。现在&lt;user&gt; 无法访问profile 文件,没有sudo 也无法修改config。一开始看起来并不重要,但相信我,如果您的项目变得更大,有人会运行脚本并弄乱您的系统。

推荐

我的建议是:请求用户验证是否是 sudoer。然后,将sudo 添加到需要它的命令中。

将此更改应用于脚本将如下所示:

#!/bin/bash
E_NOTROOT=87 # Non-root exit error.

## Prevent the execution of the script if the user has no root privileges
## Check if is sudoer
if ! $(sudo -l &>/dev/null); then
    echo 'Error: root privileges are needed to run this script'
    exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
sudo service mysql start
...

此修改允许用户像这样运行脚本:

install-app.sh

将要求用户输入他的密码以验证是否为 sudoer。之后,mkdir -vp ~/app/init会在用户家创建文件:

/home/<user>/app/init
/home/<user>/app/init/config
/home/<user>/app/init/profile

另外,我建议获取用户 homer 目录并将其用作常量。

## Defines user home directory
USER_HOME_DIR=$(getent passwd $SUDO_USER:-$USER | cut -d: -f6)
...
mkdir -vp "$USER_HOME_DIR/app/init"
...

【讨论】:

【参考方案2】:

一个班轮:

test `whoami` != "root" && echo Please run as root && exit 1

在 Debian、Ubuntu 和 Docker 下测试。

【讨论】:

注意whoami 周围的反引号,它插入了whoami 命令的输出。【参考方案3】:

在这个答案中,请明确一点,我认为读者能够阅读 bash 和 POSIX shell 脚本,如 dash

我相信这里没有太多要解释的,因为投票率很高的答案很好地解释了其中的大部分内容。

不过,如果有什么要进一步解释的,请不要犹豫发表评论,我会尽力填补空白。


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

新解决方案:

# 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!'
    exit 0 # implicit, here it serves the purpose to be explicit for the reader
else
    echo 'You are just an ordinary user.' >&2
    exit 1
fi

结论

尽管您可能不喜欢它,但 Unix/Linux 环境已经多样化了很多。这意味着有些人非常喜欢bash,他们甚至没有想到可移植性(POSIX shells)。像我这样的其他人更喜欢POSIX shell。如今,这是个人选择和需求的问题。

【讨论】:

【参考方案4】:

使用 id -u, $EUID 和 whoami 的问题是,当我伪造根目录时,它们都给出了误报,例如:

$ fakeroot

身份证号:

$ id -u
0

EUID:

$ echo $EUID
0

谁阿米:

$ whoami
root

然后一个可靠的黑客方法是验证用户是否有权访问 /root 目录:

 $ ls /root/ &>/dev/null && is_root=true || is_root=false; echo $is_root

【讨论】:

【参考方案5】:

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

[[ stuff ]] 语法是在 bash 中运行检查的标准方法。

error() 
  printf '\E[31m'; echo "$@"; printf '\E[0m'


if [[ $EUID -eq 0 ]]; then
    error "Do not run this as the root user"
    exit 1
fi

这也假设你想在失败时以 1 退出。 error 函数可以将输出文本设置为红色(不需要,但如果你问我,它非常优雅)。

【讨论】:

error 命令是什么?我的系统好像没有。 . . 哦,这只是一件小事。没关系,但我会附上它。 太酷了!如果我可以提出一些改进:(1)将换行符移到末尾; (2) 写入标准错误而不是标准输出; (3) 仅在标准错误是终端时才设置颜色(而不是将转义字符写入日志文件或less 或诸如此类的风险); (4) 设置背景颜色,因此无论用户终端的背景颜色如何,文本都将是可读的。所以,像function error () if [[ -t 2 ]] ; then echo $'\033[31;2;47m'"$@"$'\033[0m' ; else echo "$@" ; fi &gt;&amp;2 ; 这样的东西。 (根据需要进行调整。) 不应该是“-ne”而不是“-eq”吗? @CarlosRendon Slater 的代码是为了防止某些东西以 root 身份运行。 (这与 OP 的要求相反,但检查方法是相同的。)【参考方案6】:

检查你是否是root,如果不是则退出:

if ((EUID != 0)); then
    echo "Root or Sudo  Required for script ( $(basename $0) )"
    exit
fi

或者在本例中,尝试在根位置创建一个目录,然后在提升权限后尝试。

检查您是否是 root,如果可能,请检查是否提升:

# Fails to create these dirs (needs sudo)
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)

if ((EUID != 0)); then
    echo "Granting root privileges for script ( $(basename $0) )"
    if [[ -t 1 ]]; then
        sudo "$0" "$@"
    else
        exec 1> output_file
        gksu "$0" "$@"
    fi
    exit
fi
echo "Root privileges granted..."
# Creates Dirs as it now has rights
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)

【讨论】:

【参考方案7】:

使脚本只能由 root 运行的一种简单方法是使用以下行启动脚本:

#!/bin/su root

【讨论】:

我很好奇为什么这个答案没有被评为更高?它似乎是最简单和最干净的。这里有缺点吗? @Bassinator 我相信有些人说它不会在某些 nix 上运行,但到目前为止它在我的 Linux 上完美运行。试试吧!也许这个答案在比赛中来得很晚,但我正在尝试添加对社区来说很简单的知识(而不是像这个线程上的大多数人一样给出类似的答案:/)如果你有幸读到这个和它,请投票赞成在你的机器上工作 @TamusJRoyce 如果目标是检查您是否以 root 身份运行并且不强制脚本仅以 root 身份运行,那么为什么在每个答案中,在检查是否以 root 身份运行之后,他们制作脚本exit?这些答案仍然被赞成吗?我猜人们隐含地不希望脚本以 root 身份运行。因此,我遵循这种思维方式,并给出了一个更简单的答案。但是,是的,您也可以从上面更详细的答案中学习 @alexandre1985 没错。场景:如果以 root 身份运行,也许您希望脚本在询问您希望如何安装时默认全局安装程序。否则,默认设置为本地安装。用户可以选择全局或本地安装,而不是按 Enter。但如果他们喜欢默认设置,他们可以按 Enter。并且不会提示他们输入密码。如果您是 root(每个标题),您的 shebang 将永远不会“检查”。但我仍然认为这个答案很有用。 @TamusJRoyce 你的评论完全有道理。确实是一个问题,此解决方案不适用于该用例。你打开了我的视野。你说的对。谢谢【参考方案8】:
#!/bin/bash

# GNU bash, version 4.3.46
# Determine if the user executing this script is the root user or not

# Display the UID
echo "Your UID is $UID"

if [ "$UID" -eq 0 ]
then
    echo "You are root"
else
    echo "You are not root user"
fi

编者注:如果您不需要双括号,请使用单个括号以实现代码可移植性。

【讨论】:

你还没有解释为什么你为一个已经有几个答案的 5 年前的问题提供了一个替代答案。【参考方案9】:

据我所知,正确的检查方法是:

if [ $(id -u) = "0" ]; then
    echo "You are root"
else
    echo "You are NOT root"
fi

请参阅此处的“测试根”部分:

http://linuxcommand.org/lc3_wss0080.php

【讨论】:

-eq 0替换= "0" @LinuxSecurityFreak ,如果你解释我为什么要这样做,我会替换它。【参考方案10】:

$EUID 环境变量保存当前用户的 UID。 Root 的 UID 为 0。在您的脚本中使用类似这样的内容:

if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi

注意:如果您得到2: [: Illegal number:,请检查顶部是否有#!/bin/sh,并将其更改为#!/bin/bash

【讨论】:

我认为将字符串与字符串、数字与数字进行比较更好。使用双括号,您可以直接使用 > 不带引号 [[ $EUID > 0 ]] 我收到了2: [: Illegal number: 警告,直到将其更改为 Sergio 的版本。 @thekiwi5000 只有当then 与条件在同一行时才需要分号... @StephenAngelico Sudo 应该可以工作。如果您通过 $ sudo echo $EUID 进行测试;这是一个糟糕的测试并且会失败,因为在将命令传递给 sudo 之前扩展了 $EUID。尝试将 echo $EUID 放入测试脚本并使用 sudo 运行。 $EUIDdash 中不起作用(常见的/bin/sh 实现)。【参考方案11】:

检查根目录:

ROOT_UID=0   # Root has $UID 0.

if [ "$UID" -eq "$ROOT_UID" ]
then
  echo "You are root."
else
  echo "You are just an ordinary user."
fi

exit 0

经过测试并在 root 中运行。

【讨论】:

【参考方案12】:

0- 阅读官方 GNU Linux 文档,有很多正确的方法。

1- 确保放置 shell 签名以避免解释错误:

 #!/bin/bash

2- 这是我的脚本

#!/bin/bash 

if [[ $EUID > 0 ]]; then # we can compare directly with this syntax.
  echo "Please run as root/sudo"
  exit 1
else
  #do your stuff
fi

【讨论】:

【参考方案13】:

很简单的方法:

if [ "$(whoami)" == "root" ] ; then
    # you are root
else
    # you are not root
fi

使用它而不是id 的好处是您可以检查某个非root 用户是否也在运行该命令;例如。

if [ "$(whoami)" == "john" ] ; then
    # you are john
else
    # you are not john
fi

【讨论】:

如果您使用字符串“root”测试用户,您是否不允许运行名为“root”的任何人? 你忘记了';'在 if 之后 @peteroak 除了root,还有谁会被命名为root? +1 用于实际回答有关whoami 的问题,而不是使用idwhoami 命令还可用于按名称检查 other 用户而不是 root。【参考方案14】:

已经给出了一些答案,但似乎最好的方法是使用:

id -u 如果以 root 身份运行,将返回 0 的 id。

这似乎比其他方法更可靠,并且即使脚本通过sudo 运行,它似乎也返回 0 的 id。

【讨论】:

【参考方案15】:
if [[ $(id -u) -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi

if [[ `id -u` -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi

:)

【讨论】:

你可以考虑把Jeremy J Starcher's answer和这部分结合起来,最后总是一样的东西...... 我知道,在终端上,当我忘记在前面加上 sudo 前缀时,我可以简单地输入 sudo !!,它就可以为我工作,而不是按向上箭头,转到行的开头,并手动添加sudo 。不确定这是 BASH 的事情还是 SUDO 的事情或其他,但它在大多数情况下都有效。 @0xSheepdog:这是一个 Bash 的东西(以及其他一些 shell 的一个特性,例如 csh,它起源于 IIRC)。 总而言之,我会将反引号部分作为一个整体删除,并将 ok 行重新写入if [ "$(id -u)" -ne 0 ]; then echo 'Please run as root.' &gt;&amp;2; exit 1; fi。干杯。【参考方案16】:

在 bash 脚本中,您有多种方法可以检查正在运行的用户是否为 root。

作为警告,不要使用root 用户名检查用户是否为root。没有任何东西可以保证 ID 为 0 的用户被称为 root。这是一个被广泛遵循的非常严格的约定,但任何人都可以将超级用户重命名为另一个名称。

我认为使用 bash 时最好的方法是使用手册页中的$EUID

EUID   Expands to the effective user ID of the current  user,  initialized
       at shell startup.  This variable is readonly.

这是一种比$UID 更好的方法,$UID 可以更改并且不反映运行脚本的真实用户。

if (( $EUID != 0 )); then
    echo "Please run as root"
    exit
fi

我解决这类问题的一种方法是在我的命令中注入sudo,而不是以root身份运行。这是一个例子:

SUDO=''
if (( $EUID != 0 )); then
    SUDO='sudo'
fi
$SUDO a_command

这样,我的命令在使用超级用户时由 root 运行,或者在由普通用户运行时由 sudo 运行。

如果您的脚本始终由 root 运行,只需相应地设置权限 (0500)。

【讨论】:

如果尝试运行脚本的人没有sudo权限,这样会导致报错,命令无法运行?只是想确定一下,我理解正确。 我不确定这个答案是否 100% 完全正确。在使用 Bash 4.2.46 的 RHEL7.2 上...如果我 printf $EUID 使用或不使用 sudo,我会取回自己的 UID。如果我使用 id -u 我得到我的 UID,当我用 sudo 调用它时,我得到 0 回来。我是不是做错了什么? @0xSheepdog,有可能在 bash cli 上,首先解析 $EUID 变量扩展,然后 sudo 更改用户。因此你会得到这种行为,但在脚本中,一切仍然可以正常工作。 @AlexanderBird 好点,谢谢!我没有做任何真正的测试,只是报告了我的经验。 在您的测试中使用“herestring”来防止变量的本地 shell 扩展。比较 sudo bash &lt;&lt;&lt;'echo $EUID'bash &lt;&lt;&lt;'echo $EUID'。在tldp.org/LDP/abs/html/x17837.html 了解更多关于heredoc-like herestring【参考方案17】:

试试下面的代码:

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

if [ `id -u` != "0" ]; then
    echo "Sorry, you are not root."
    exit 1
fi

【讨论】:

【参考方案18】:

id -uwhoami 好得多,因为某些系统如 android 可能不提供 root 这个词。

例子:

# whoami
whoami
whoami: unknown uid 0

【讨论】:

【参考方案19】:

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

此外,通过正确使用sudo,您可以检查脚本并查看它是否以root 身份运行。如果没有,让它通过sudo 重新调用自己,然后以root 权限运行。

根据脚本的作用,另一种选择可能是为脚本可能需要的任何专用命令设置sudo 条目。

【讨论】:

是否有一种优雅的方式让程序自行调用?也许有一些带有程序绝对路径的bash变量? 我现在不在 BASH 提示符附近进行测试,但 $0 有运行脚本的名称。有一些示例here 可能对您有所帮助。【参考方案20】:

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

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

【讨论】:

如果有人有他们的权限,这很好用,但我觉得过度使用 777 炸弹会使这个检查对于那些首先会犯错误的用户来说有点错误. 这是假设用户正在运行一个可执行脚本。如果用户只是调用bash /path/to/script,它仍然可以运行,即使o=r

以上是关于如何检查是不是在 bash 脚本中以 root 身份运行的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Bash 脚本中检查程序是不是存在?

如何使用 adb 在 bash 脚本中以读写方式重新安装我的 Android/系统?

如何在不运行Bash脚本的情况下语法检查?

bash 命令检查目录是不是可执行

如何创建一个 bash 脚本来检查 SSH 连接?

检查 Bash shell 脚本中是不是存在输入参数