如何检查是不是在 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
如你所知,她的剧本一团糟。现在<user>
无法访问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 >&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 运行。
$EUID
在dash
中不起作用(常见的/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
的问题,而不是使用id
; whoami
命令还可用于按名称检查 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.' >&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 <<<'echo $EUID'
和 bash <<<'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 -u
比whoami
好得多,因为某些系统如 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 身份运行的主要内容,如果未能解决你的问题,请参考以下文章