76

要在当前 shell 中执行 shell 脚本,我们需要使用句点.source命令。但是为什么它不能在获得sudo许可的情况下工作?

我有一个具有执行权限的脚本,名为setup.sh. 当我使用句号时,我得到这个:

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

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

提前致谢..

4

9 回答 9

158

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

sudo bash script.sh

似乎对我有用。

于 2014-05-07T00:50:56.310 回答
38

我认为您对采购和执行脚本之间的区别感到困惑。

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

采购脚本只能与 bash 脚本一起使用(如果您正在运行 bash)。它有效地键入命令,就像您执行它们一样。这很有用,因为它允许脚本更改 shell 中的环境变量。


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

获取脚本不使用 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
于 2013-09-15T07:02:11.763 回答
37

您尝试做的事情是不可能的;您当前的 shell 在您的常规用户 ID 下运行(即没有 root 访问权限sudo会给您),并且无法授予它 root 访问权限。什么sudo是创建一个以 root 身份运行的新 *sub* 进程。子进程可以只是一个常规程序(例如在根进程中sudo cp ...运行cp程序),也可以是根子shell,但不能是当前shell。

(实际上比这更不可能,因为sudo命令本身是作为当前 shell 的子进程执行的——这意味着从某种意义上说,它在“当前 shell”中做任何事情已经太晚了,因为那不是它所在的地方执行。)

于 2013-09-15T17:35:12.040 回答
5

基本上 sudo 期望,一个可执行文件(命令)跟随&你提供一个.

因此错误。

试试这个方法$ sudo setup.sh


于 2013-09-15T06:41:56.887 回答
2

如果你真的想“在当前 shell 中使用 sudo 权限执行调用 shell 脚本”,你可以使用exec...

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

我坚持用“调用”代替“执行”,因为前者的含义包括创建一个新的流程和ID,而后者是模棱两可的,留有创造的空间,我对此很满意。

考虑这个测试用例并仔细查看 pid 1337

# 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
于 2017-01-21T00:30:21.463 回答
2

它可以在没有“ sudo ”的情况下工作。

bash setup.sh
于 2021-03-02T00:26:17.293 回答
1

这里的答案解释了它为什么会发生,但我想我会添加我的简单方法来解决这个问题。首先,您可以将文件放入具有 sudo 权限的变量中。然后,您可以评估变量以在当前 shell 中执行文件中的代码。

这是读取和执行 .env 文件的示例(前 Docker)

 sensitive_stuff=$(sudo cat ".env")
 eval "${sensitive_stuff}"
 echo $ADMIN_PASSWORD 
于 2019-12-31T15:09:27.007 回答
1

最简单的方法是输入:

sudo /bin/sh example.sh
于 2017-05-26T00:35:16.497 回答
1

即使是第一个答案也绝对出色,您可能只想在 sudo 下运行脚本。

您必须指定绝对路径,例如:

sudo /home/user/example.sh
sudo ~/example.sh

(两者都在工作)

这行不通!

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

它总会回来

sudo: bin/sh: command not found
sudo: example.sh: command not found
于 2019-01-10T17:49:10.800 回答