我的环境是Ubuntu 12.04 LTS,sudo
版本是1.8.3p1。
首先我以普通用户身份登录:
$ whoami
fin
$ cat /etc/passwd | grep -i "root\|fin"
root:x:0:0:root:/root:/bin/bash
fin:x:1000:1000:This is a normal user:/home/fin:/bin/bash
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Mar 30 2012 /bin/sh -> dash
$ ls -l /bin/bash
-rwxr-xr-x 1 root root 920788 Apr 3 2012 /bin/bash
$ echo $SHELL
/bin/bash
$ ps | grep "$$" | awk '{ print $4 }'
bash
$ ls -l ./test.sh
-rwxr-xr-x 1 fin fin 37 Sep 27 16:46 test.sh
$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'
$ ./test.sh
bash
$ sudo ./test.sh
sh
我想最后的输出也应该是bash
因为/etc/passwd
显示 root 使用bash
,我是否遗漏了任何要点sudo
?
答案1
它的使用_PATH_BSHELL
类似于execvp()
Linux 上的定义,/bin/sh
如/usr/include/paths.h
.这应该与使用env
or执行时相同find -exec
。
它当然不应该使用用户的登录 shell。您在上面看到的事实bash
是因为它bash
(您输入该命令行的 shell)尝试执行它,并且当它ENOEXEC
从中获取错误代码时execve
,它决定用自己来解释它(在sh
兼容模式下)。
答案2
因为您不使用-s
选项,所以sudo
将使用(在 Ubuntu 12.04 LTS 中_PATH_BSHELL
定义)来设置它运行。查看源代码:/usr/include/paths.h
$SHELL
sudo
/* Stash user's shell for use with the -s flag; don't pass to plugin. */
if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
}
如果您使用-s
选项,sudo
将使用您的$SHELL
而不是_PATH_BSHELL
:
$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'
$ ./test.sh
bash
$ sudo -s ./test.sh
bash
答案3
内核只能运行二进制可执行映像。那么脚本如何运行呢?毕竟,我可以my_script_without_shebang
在 shell 提示符下键入并且不会收到ENOEXEC
错误。脚本的执行不是由内核完成的,而是由 shell 完成的。 shell 中的执行代码通常类似于:
/* try to run the program */
execl(program, basename(program), (char *)0);
/* the exec failed -- maybe it is a shell script without shebang? */
if (errno == ENOEXEC)
execl ("/bin/sh", "sh", "-c", program, (char *)0);
您可以通过跟踪虚拟 shell 脚本(不使用 shebang)来验证这一点:
cat > /tmp/foo.sh <<EOF
echo
EOF
chmod u+x /tmp/foo.sh
strace /tmp/foo.sh 2>&1 | grep exec
execve("/tmp/foo.sh", ["/tmp/foo.sh"], [/* 28 vars */]) = -1 ENOEXEC (Exec format error)
然后,按照 Stephane 的描述继续执行 - 使用默认 shell(在上面的代码片段中是硬编码的)。这个不错的 UNIX 常见问题解答可以回答更多。