根据须藤(8):
Process model
当 sudo 运行命令时,它会调用 fork(2),如上所述设置执行环境,并在子进程中调用 execve 系统调用。
另外,我发现分叉的子进程使用sh
.
因此,如果该命令是一个 bash 脚本,其中包含一些特定于 bash 的命令(例如source
其中的命令),则将sh
无法正确执行它。例如:
% 猫 /tmp/wibble 来源某物 % ls -l /tmp/wibble -rwxr-xr-x 1 用户 用户 17 Aug 24 08:32 /tmp/wibble % 获取密码 root 根:x:0:0:根:/根:/bin/bash % /tmp/摆动 /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 % /bin/bash /tmp/wibble ~ [pts/3.4028.1] /tmp/wibble: 第 1 行: 某事: 没有这样的文件或目录 % /bin/dash /tmp/wibble /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 % /bin/sh /tmp/wibble /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 %回显$SHELL /bin/zsh % sudo /tmp/wibble /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 % sudo -s /tmp/wibble /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 % sudo -i /tmp/wibble /tmp/wibble: 第 1 行: 某事: 没有这样的文件或目录 % 导出 SHELL=/bin/bash % sudo /tmp/wibble /tmp/wibble: 1: /tmp/wibble: 来源: 未找到 % sudo -s /tmp/wibble /tmp/wibble: 第 1 行: 某事: 没有这样的文件或目录 % sudo -i /tmp/wibble /tmp/wibble: 第 1 行: 某事: 没有这样的文件或目录 %
通常我们可以附加一个-s
选项来sudo
解决这个问题,如前面的示例所示,但我想知道为什么sudo
使用sh
默认值。这样可以配置到其他shell吗?
答案1
确保您的脚本以适当的#!
-line 开头。
如果脚本是可执行的并且以例如
#!/bin/bash
(或者您系统上的任何路径bash
),然后是脚本将要/bin/bash
如果您键入,则将被解释为sudo script
,就像 Python 脚本/usr/bin/python
如果其第一行是则将被解释为#!/usr/bin/python
。
sh
是 shellbash
还是其他 shell 的问题在这里并不有趣。一般来说,假设每种类型的 shell 都有自己互斥的脚本语言并使用#!
-line 来准确指定您编写脚本的解释器。例如,如果您仅使用 POSIX 语法和句法编写可移植脚本,请使用#!/bin/sh
,但#!/bin/bash
如果您使用bash
的数组或此 shell 扩展 POSIX shell 的其他内容,请使用 。
当文本文件不以#!
,开头时,/bin/sh
用作后备。这与 无关sudo
。
在更新的问题中,您显示了对没有 -line 的脚本的多次调用#!
。它们都失败,要么是因为解释器不理解source
命令 ( source: not found
),要么是因为它找不到文件 ( something: No such file or directory
)。
总结一下:始终使用 -line 指定脚本的解释器#!
。
另外:使用时source
,请指定源文件的路径,即使它只是./something
,否则可能会从$PATH
.
有关的:
答案2
更新:问题已被大量修改,所以必须是我的答案。
虽然我发现没有标准定义什么被认为是“脚本”,但只要它由 shell 执行,即使只是包含命令列表的文件也可能是“脚本”。因此,应该小心,如果脚本顶部没有指定 shell,则将采用默认行为(sh)。
旧答案:
如果sh
当前系统上不是 bash,脚本应该失败,因为 bashisms 不会被理解。
我们来做个实验吧!
~/Downloads/test$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 8月 31 2016 /bin/sh -> dash
首先,确认sh
不是bash
。
bash.sh
将要利用bash
.它调用另一个 bash 脚本,使用sudo
.
:~/Downloads/test$ cat bash.sh
#!/bin/bash
sudo ./sudo_bash.sh
dash.sh
是一个破折号脚本,它表明((i++))
根据你的话, bashism 应该失败。
:~/Downloads/test$ cat dash.sh
#!/bin/sh
i=0
((i++))
echo $i
我们来确认一下
:~/Downloads/test$ ./dash.sh
./dash.sh: 4: ./dash.sh: i++: not found
0
:~/Downloads/test$
美丽地失败了。现在,sudo_bash.sh
是一个 bash 脚本。我们将使用 sudo 命令从 bash.sh 执行它,正如你所说,如果它真的被分叉了,sh
那么它也应该失败。
:~/Downloads/test$ cat sudo_bash.sh
#!/bin/bash
i=0
((i++))
echo $i
:~/Downloads/test$ ./bash.sh
[sudo] password for cs-server:
1
:~/Downloads/test$
执行成功。那就证明新环境不能用了/bin/sh
。
I'd like to know why sudo uses sh as default.
历史上第一个 shell 是 sh,因此出于兼容性原因仍保留名称。
Is it so that it can be configured to other shells?
当然,只需更改 sh 的符号链接即可
:~/Downloads/test$ cat text_file
i=0
((i++))
echo $i
:~/Downloads/test$ chmod +x text_file
:~/Downloads/test$ sudo ln -sf bash /bin/sh
:~/Downloads/test$ sudo ./text_file
1
:~/Downloads/test$ sudo ln -sf dash /bin/sh
:~/Downloads/test$ sudo ./text_file
./text_file: 2: ./text_file: i++: not found
0