使用sudo权限在当前shell中执行一个shell脚本

94

要在当前Shell中执行一个shell脚本,我们需要使用点号.或者source命令。但是为什么使用sudo权限时无法工作?

我有一个具有执行权限名为setup.sh的脚本。当我使用点号时,我会得到以下结果:

$ sudo . ./setup.sh 
sudo: .: command not found

source 命令也会产生类似的错误。我错过了什么吗?我应该怎么做才能在同一个/当前的 shell 中以 sudo 权限运行脚本?


尽管答案很好,但我认为问题几乎只是一个打字错误。删除那个单独的第一个句点。 - beroe
3
@beroe 您错了。问题不在于打字错误。如果 setup.sh 没有执行权限,则必须明确地使用 bash 解释它。这可以通过 bash setup.shsource setup.sh. setup.sh 来完成。但是,由于后两种是 bash 内置命令,因此只能使用第一种形式与 sudo 一起使用,sudo 需要可执行文件。您可以使用 sudo which bashsudo which sourcesudo which . 来查看 sudo 将找到什么。 - Bruno Bronosky
@BrunoBronosky,我觉得他们试图将第一个孤立的点作为命令运行。由于这指定了当前工作目录,而不是命令,因此会引发该错误。如果他们输入./setup.sh,那就是另一回事了。 - beroe
3
@beroe 一个孤立的点是一个 shell 命令 —— 具体来说,它是一个 shell 内置命令,等效于 source 命令(也是内置命令)。 - Gordon Davisson
10个回答

181

我不确定这是否违反了任何规定,但是

sudo bash script.sh

对我来说似乎有效。


1
这个命令不会像source或者.一样在同一个/当前的shell中运行脚本。它会作为sudo的子进程运行一个新的bash shell(而sudo本身是当前shell的子进程)。对于大多数脚本来说这是可以的(这是运行脚本的正常方式),但是对于必须在当前shell中运行的脚本来说,它无法实现。 - undefined

46
你试图做的事情是不可能的;你当前的shell正以普通用户ID运行(即没有sudo权限),而且无法授予它root权限sudo所做的是创建一个新的*子*进程以root身份运行。该子进程可以只是一个常规程序(例如,sudo cp ...在root进程中运行cp程序)或者它可以是一个root子shell,但它不能是当前的shell。

(实际上,这比那还要不可能,因为sudo命令本身被执行为当前shell的子进程 - 这意味着从某种意义上说,它已经太晚了,因为它不能在“当前shell”中执行任何操作。)


7
我不确定为什么这个被接受的答案是正确的,因为下面@JaseC的答案对我来说似乎行得通。sudo bash myscript.sh正好实现了我想要的,并且也是OP似乎在寻找的。 - russell
30
sudo bash myscript.sh 命令将在子shell中运行脚本,而不是当前shell中运行。如果这种方式适用于您的情况,那就很好——但是这个问题特别指的是在当前shell中以root身份运行该脚本。 - Gordon Davisson
4
由于不准确的用词,整个前提存在歧义。“要在当前 shell 中执行 shell 脚本,我们需要使用一个句点。” 不,那个句点并不执行,它是源、插入、读取或解释。执行意味着一个不同的 PID。因此,我们不清楚他们是否关心他们当前 shell 的 PID 或为什么他们会关注。 - Bruno Bronosky

41

我认为你对脚本的引用(sourcing)和执行(executing)有所混淆。

执行脚本意味着创建一个新进程并运行程序。该程序可以是shell脚本或其他类型的程序。由于它是子进程,因此在程序中更改的任何环境变量都不会影响shell。

只能使用bash脚本(如果您正在运行bash),才能使用脚本引用。它有效地将命令键入,就像您自己输入一样。这很有用,因为它允许脚本更改shell中的环境变量。


运行脚本很简单,只需输入脚本的路径即可。 . 表示当前目录。因此, ./script.sh 将执行当前目录中的文件 script.sh 。如果命令是单个文件(例如 script.sh ),它将检查路径变量PATH中的所有文件夹以查找脚本。请注意,当前目录不在PATH中,因此您无法通过运行 script.sh 来执行当前目录中的文件 script.sh ,您需要运行 ./script.sh (除非当前目录在PATH中,例如您可以在/bin目录中运行 ls )。

脚本引用不使用PATH,只是搜索路径。请注意, source 不是程序 - 否则它无法更改当前shell中的环境变量。实际上,它是一个bash内置命令。搜索 /bin /usr/bin - 您不会在那里找到 source 程序。因此,要在当前目录中引用文件 script.sh ,您只需使用source script.sh


sudo如何与此交互? sudo接受一个程序,并将其作为root执行。例如, sudo ./script.sh 以root身份运行 script.sh 子进程。

然而,sudo source ./script.sh做什么呢?请记住, source 不是程序(而是shell内置程序)?但是sudo需要一个程序名,因此它会搜索名为 source 的程序。它找不到,因此失败了。无法在以root身份运行的文件中进行源操作,而不创建新的子进程,因为在启动后,您无法更改程序的运行者(在这种情况下是bash)。

我不确定您实际想要什么,但希望这可以为您澄清问题。


这里有一个具体的例子。在当前目录中创建名为 script.sh 的文件,其中包含以下内容:

#!/bin/bash    
export NEW_VAR="hello"
whoami
echo "Some text"

使用命令 chmod +x script.sh 使其可执行。

现在观察bash的运行情况:

> ./script.sh
david
Some text
> echo $NEW_VAR

> sudo ./script.sh
root
Some text
> echo $NEW_VAR

> source script.sh
david
Some text
> echo $NEW_VAR
hello
> sudo source script.sh
sudo: source: command not found

5

基本上sudo会期望有一个可执行文件(命令)后面跟着你提供的.

因此会出现错误。

尝试这样做:$ sudo setup.sh



3

它可以在不使用"sudo"的情况下工作。

bash setup.sh

3
如果你真的想要在当前shell中以sudo权限"执行调用一个shell脚本",你可以使用exec来...

替换shell为给定程序(执行它,而不是作为新进程)

我坚持将“执行”替换为“调用”,因为前者的含义包括创建一个新的进程和ID,而后者是模糊的,留下了创意的空间,而我充满了创意。
考虑这个测试案例,并仔细看pid1337
# Don't worry, the content of this script is cat'ed below
$ ./test.sh -o foo -p bar

User ubuntu is running...
 PID TT       USER     COMMAND
 775 pts/1    ubuntu   -bash
1408 pts/1    ubuntu    \_ bash ./test.sh -o foo -p bar
1411 pts/1    ubuntu        \_ ps -t /dev/pts/1 -fo pid,tty,user,args

User root is running...
 PID TT       USER     COMMAND
 775 pts/1    ubuntu   -bash
1337 pts/1    root      \_ sudo ./test.sh -o foo -p bar
1412 pts/1    root          \_ bash ./test.sh -o foo -p bar
1415 pts/1    root              \_ ps -t /dev/pts/1 -fo pid,tty,user,args

Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)

#!/usr/bin/env bash

echo; echo "User $(whoami) is running..."
ps -t $(tty) -fo pid,tty,user,args

if [[ $EUID > 0 ]]; then
    # exec replaces the current process effectively ending execution so no exit is needed.
    exec sudo "$0" "$@"
fi

echo; echo "Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)"; echo
cat $0

这是另一个使用 sudo -s 的测试。

$ ps -fo pid,tty,user,args; ./test2.sh
  PID TT       USER     COMMAND
10775 pts/1    ubuntu   -bash
11496 pts/1    ubuntu    \_ ps -fo pid,tty,user,args

User ubuntu is running...
  PID TT       USER     COMMAND
10775 pts/1    ubuntu   -bash
11497 pts/1    ubuntu    \_ bash ./test2.sh
11500 pts/1    ubuntu        \_ ps -fo pid,tty,user,args

User root is running...
  PID TT       USER     COMMAND
11497 pts/1    root     sudo -s
11501 pts/1    root      \_ /bin/bash
11503 pts/1    root          \_ ps -fo pid,tty,user,args

$ cat test2.src
echo; echo "User $(whoami) is running..."
ps -fo pid,tty,user,args

$ cat test2.sh
#!/usr/bin/env bash

source test2.src

exec sudo -s < test2.src

并且还有一个使用sudo -s的更简单的测试方法

$ ./exec.sh
bash's PID:25194    user ID:7809
systemd(1)───bash(23064)───bash(25194)───pstree(25196)

Finally...
bash's PID:25199    user ID:0
systemd(1)───bash(23064)───sudo(25194)───bash(25199)───pstree(25201)

$ cat exec.sh
#!/usr/bin/env bash

pid=$$
id=$(id -u)
echo "bash's PID:$pid    user ID:$id"
pstree -ps $pid

# the quoted EOF is important to prevent shell expansion of the $...
exec sudo -s <<EOF
echo
echo "Finally..."
echo "bash's PID:\$\$    user ID:\$(id -u)"
pstree -ps $pid
EOF

3
今天我收到了一次没有任何评论的负评。这对任何人有什么帮助呢?享受失去你的2个声望点吧。(没错,为了给我一个负评,你的声誉下降了2点,而我只失去了1个声望点。) - Bruno Bronosky
有点混乱...下次,请将命令和输出直接放在一起,而不是描述您所做的事情。这将有助于解释脚本中使用的命令,因为并非每个人都具有您的熟练程度。 - pablo.vix

1
这里的回答解释了为什么会发生这种情况,但我想补充一下我简单的解决方法。首先,您可以使用sudo权限将文件连接到变量中。然后,您可以评估该变量以在当前shell中执行文件中的代码。
以下是读取和执行.env文件(例如Docker)的示例。
 sensitive_stuff=$(sudo cat ".env")
 eval "${sensitive_stuff}"
 echo $ADMIN_PASSWORD 

1
即使第一个答案绝对精彩,您可能只想在sudo下运行脚本。
您必须指定绝对路径,如下所示:
sudo /home/user/example.sh
sudo ~/example.sh

两个都在工作

THIS WONT WORK!

sudo /bin/sh example.sh
sudo example.sh

它将始终返回。
sudo: bin/sh: command not found
sudo: example.sh: command not found

0

最简单的方法是输入:

sudo /bin/sh example.sh

即使example.sh在同一目录下,也不会出现“无法打开example.sh:没有这样的文件”的错误。 - Owl

0

sudo -s应该能解决问题

例如,脚本必须以普通用户身份运行,然后作为sudo执行命令(无需密码)

ubuntu@xoan:~/scripts$ bash rp_filter.sh

#!/bin/bash
juju ssh 9 sudo -s sysctl -w net.ipv4.conf.all.rp_filter=0 && sysctl -p

ubuntu@xoan:~/scripts$ bash rp_filter.sh
net.ipv4.conf.all.rp_filter = 0 (<- confirms set loose)

Connection to 192.168.1.189 closed.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接