脚本如何检查它是否以root身份运行?

我正在编写一个简单的bash脚本,但我需要检查它是否以root权限运行。我知道可能有一种非常简单的方法来做到这一点,但我不知道怎么做。
只是为了明确:
有什么简单的方法可以编写一个名为foo.sh的脚本,使得命令./foo.sh输出0,而命令sudo ./foo.sh输出1
11个回答

#!/bin/bash
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root" 
   exit 1
fi

7我接受这个原因是因为(经过一些测试),使用系统变量EUID比运行命令(id -u)要快(大约快100倍),但所有的答案都很好。 - Malabarba
谢谢 aneeshep。我甚至不知道有一个用于有效用户ID的环境变量。 - Delan Azabani
1要小心哦!一个程序可以临时放弃其超级用户特权,此时$UIDid -ru仍然为0,但$EUIDid -u不是。请参考getuid(2)并决定您需要什么。 - David Foerster
24此外,$UID$EUID是bashism。仅符合POSIX标准的shell没有这些变量,您需要使用id(1)代替。 - David Foerster
5在一个 bash 脚本中,你可以使用 if ((EUID)); then,参考 @geirha 的评论 - jfs
1这不符合问题的要求。作为第一个“普通”用户运行echo $EUID,会得到1000(预期)。但是,运行sudo echo $EUID会得到1000(不是预期的结果)。另一方面,id -u会给我返回1000,而sudo id -u会给我返回0(两者都是预期的结果)。 - Andrew Ensley
9@Andrew:变量在传递给sudo之前被替换。如果你运行sudo sh -c 'echo $EUID',你会看到你期望的结果。 - sourcenouveau
@M.Dudley 这说得通。如果可以的话,我会撤回我的踩票。然而,sudo sh -c 'echo $EUID'给我一个空行。我应该看到0 - Andrew Ensley
8@Andrew - 我知道我来晚了,但为了后人:$EUID 是一个 Bashism(如上面 @DavidFoerster 所提到的),而你的 /bin/sh 很可能是一个指向 /bin/dash 而不是 /bin/bash 的链接。sudo bash -c 'echo $EUID' 将会得到预期的结果。 - Adrian Günter
即使是指向 /bin/bash 的链接,许多(甚至全部?)Bash 安装在以 sh 形式调用时会运行在 POSIX 模式下。也就是说,它们会有意忽略 Bashisms。这对于编写符合 POSIX 标准的 shell 脚本来说是一个很有用的初步测试,但如果你使用 sh -c 来延迟插值,可能会导致问题。 - anon
可以像一行命令这样使用,例如 [[ $EUID -ne 0 ]] && echo "非 root 用户" || echo "root 用户" - fcole90

一个root用户不一定要被命名为"root"。`whoami`返回具有用户ID为`0`的第一个用户名。`$USER`包含已登录用户的名称,该用户可以具有用户ID为`0`,但名称可能不同。
唯一可靠的程序来检查账户是否以root身份登录或非root身份登录:
id -u

我使用-u来表示有效用户ID,而不是用-r表示真实用户ID。权限由有效用户ID决定,而非真实用户ID。

测试

/etc/passwd以给定顺序包含以下用户名和用户ID 0

rootx
root2

已登录为root2,给出以下结果:

  • whoamirootx
  • echo $USERroot2(如果程序是在空环境中启动的,则返回空字符串,例如env -i sh -c 'echo $USER'
  • id -u0。如您所见,其他程序在此检查中失败,只有id -u通过。

更新后的脚本如下:

#!/bin/bash
if ! [ $(id -u) = 0 ]; then
   echo "I am not root!"
   exit 1
fi

5我总是尽量保持尽可能符合POSIX标准,使用shdash)作为解释器而不是bash。但是很好的尝试,避免了另一个分支 :) - Lekensteyn
在昏迷了一段时间后,我回来看了一眼。Lekensteyn的方法确实似乎更好。他的回答现在是新的被接受的回答。给你加1分,Lekensteyn,特别是能够获得这个问题上那个难以获得的徽章 ;) - Thomas Ward
谢谢你等这么久来接受我的问题:D 我得到了一个金色民粹主义者徽章和一个铜色的好答案徽章,因为这个答案:)我想知道这个问题是如何变得那么受欢迎的,在一天内有345次浏览! - Lekensteyn
我还是喜欢使用这个测试:[ -w /etc/shadow ]。如果这是真的,你也可以成为root用户。 ;) - Kees Cook
3我见过更短的版本,[ -w / ]。思路是一样的。注意:在某些chroot环境中,[ -w /etc/shadow ]会失败,因为/etc/shadow不存在,所以使用/的方法更可取。更好的方式是检查是否真正需要root权限。如果脚本需要写入/etc/someconfig,只需检查该文件是否可写或者文件不存在且/etc可写。 - Lekensteyn
是的,这是一个很好的答案。 - César

@Lekensteyn 所说,您应该使用有效的用户ID。您不需要在bash中调用id -u

#!/bin/bash

if [[ $EUID -ne 0 ]]; then
   echo "You must be root to do this." 1>&2
   exit 100
fi

@geirha在评论中的建议使用了算术计算:

#!/bin/bash

if (( EUID != 0 )); then
   echo "You must be root to do this." 1>&2
   exit 100
fi

9通常应使用((..))来测试数字,而使用[[..]]来测试字符串和文件,所以我会改为使用if ((EUID != 0)); then,或者只需if ((EUID)); then - geirha
@geirha:我已经采纳了你的建议。 - jfs
2EUID 应该变成 $EUID。不要使用 (( $EUID != 0 )),而是使用 [ $EUID != 0 ]。这样也适用于 dash - Lekensteyn
2@Lekensteyn:EUID运行得很好。问题标记为bash而不是dash。命令env -i sh -c '[ $EUID != 0 ]'失败了,但是env -i bash -c '(( EUID != 0 ))'可以工作。顺便说一下,在Ubuntu上,dash对于一些非ASCII字符无法正常工作。http://stackoverflow.com/questions/5160125/how-to-create-a-non-ascii-file-using-redirection-with-sh-c 现在已经是2011年了,还有人使用其他语言。 - jfs
在无聊的时候回顾了一下,经过测试,我决定从现在开始使用这里列出的方法,并加入$EUID的相关内容。由于这个原因,我已经改变了接受的答案。 - Thomas Ward
这些示例对我在12.04上都不起作用。要么出现EUID:未找到,要么出现[[: 未找到。但是[ $(id -u) = 0 ]运行得非常好。 - blee
@briankb:这些例子是有效的。请确保使用bash,而不是dash。 - jfs

你可以通过使用whoami命令来实现这一点,该命令会返回当前用户:
#!/bin/bash

if [ `whoami` != 'root' ]
  then
    echo "You must be root to do this."
    exit
fi

...

运行上述代码,如果当前用户不是root,则会打印出"You must be root to do this."。

注意:在某些情况下,另一种方法是直接检查$USER变量:

if [ $USER != 'root' ]

我会看一下代码,看看它是否会导致脚本失败或其他问题。如果它能正常工作,我会接受你的答案 :) - Thomas Ward
把这个放到我的脚本里,它运行得非常好。非常感谢。:) - Thomas Ward
4@George Edison: 我建议不要使用$USER,特别是以这种方式。$USER变量是由登录shell设置的,并不一定会传递给程序(env -i sh -c 'echo $USER')。以这种方式,$USER为空,会导致语法错误。 - Lekensteyn
2@Lekensteyn 不仅 $USER 是由 shell 设置的,它还是容易伪造的东西。Whoami 将返回实际用户。 - Paul de Vrieze
2@PauldeVrieze 搞这个检查有什么意义呢? - htorque
2id -u 是一个更优秀的解决方案。我认为这个答案可能会引导错误,不应该被接受。 - sam hocevar
andrew@andrew-testing:〜$ sudo whoami 根 andrew@andrew-testing:〜$ sudo echo $ USER 安德鲁 - andrewsomething
@Sam:为什么会误导呢? - Nathan Osman
2@andrewsomething: $USER 在你的shell中被解释,你的例子应该是 sudo sh -c 'echo $USER' 才有意义。 @George: 它错误地假设 whoami 对于UID 0总是返回 root - sam hocevar

考虑到效率,您可以首先测试EUID环境变量,如果不存在,则调用标准的id命令。
if ((${EUID:-0} || "$(id -u)")); then
  echo You are not root.
else
  echo Hello, root.
fi

通过使用“OR快捷方式”,您可以避免调用系统命令,优先查询内存变量。这样做的好处是代码重用。
function amIRoot() {
  ! ((${EUID:-0} || "$(id -u)"))
}

1很抱歉,你似乎没有进行基准测试,但我已经做过了。这个命令所需的时间与 id -u 完全相同。请点击此处查看基准测试脚本以及结果:https://pastebin.com/srC3LWQy - Vlastimil Burián
3*((${EUID:-0} || "$(id -u)")) 总是运行 id -u*,就像 ((1 || $(echo hi >&2; echo 1)))((0 || $(echo hi >&2; echo 1))) 都会打印出 hi。在 shell 算术 之外,&&|| 是控制操作符;在 a || "$(b)" 中,只有当 "$(b)" 运行时,b 才会运行。大多数扩展,包括命令替换,都受控制操作符的短路语义约束。Shell 算术也有运算符,它们恰好被拼写为 &&||,同样也会进行短路处理,例如 ((1 || 0/0)) 不会报错,但是 shell 算术发生在嵌套命令替换展开之后。 - Eliah Kagan

#!/bin/bash
[[ $(id -u) != 0 ]] 
echo $?

1数字比较最好使用-ne(违反直觉:文本操作数用于数字比较)。 - Gonen

#!/bin/bash
if [ "$(id -u)" == "0" ]
then
    echo "I am root."
else
    echo "I am not root."
fi

一个简单的方法是让脚本只能由root用户运行,可以在脚本开头加上以下一行代码:
#!/bin/su root

3不要这样做。它试图直接以root身份登录,而许多系统可能需要使用sudo,因为前者是被禁止的。此外,shebang的目的是确保脚本在其设计的环境中运行,而你的方法将导致脚本在root的默认shell中运行,如果脚本由除作者之外的任何人运行,那么无法预测默认shell是什么。因此,使用shebang来提升脚本的权限会严重破坏其可移植性,并在许多系统上导致错误。(su:认证失败) - StarCrashr
1别这样做。有些恶作剧者可能会创建一个名为'root'的第二个用户,或者创建一个名为'root'但实际上并不是真正的'root'的用户,从而欺骗你的程序以为它是'root'。参见https://askubuntu.com/a/836621/1070384。 - Sapphire_Brick

这个答案只是为了保存一个可能对你们中的一些人有帮助的想法。 如果你需要从桌面GUI运行并需要root权限的脚本,请尝试以下方法:
#!/bin/bash
if ! [ $(id -u) = 0 ]; then
   gksudo -w $0 $@
   exit
fi

#here go superuser commands, e.g. (switch intel cpu into powersave mode):
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
cpupower frequency-set -g powersave

这样,您将获得一个漂亮的对话框,询问您的 root 用户密码。就像在命令行中使用 sudo 一样。
如果您的系统中没有 gksudo,请使用 sudo apt-get install gksu 进行安装。

这段代码将会:
- 检查不同的会话 - 建议使用 `sudo !!` - 并返回一个错误
如果 [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ] 那么 echo "您必须以 root 用户身份运行此脚本!" echo "使用 'sudo !!'" exit 1 fi