我在 debian 上运行,所有下面的命令(dash和bash)都输入到 bash。
在whoami似乎永远不会运行在用户测试中划线,如下面的代码。
$ sudo dash << 'end'
> su test
> whoami
> end
root
$ sudo bash << 'end'
> su test
> whoami
> end
test
Run Code Online (Sandbox Code Playgroud)
请考虑以下示例:
$ cat f
grep pos /proc/self/fdinfo/0
IFS= read -r var
echo A
echo B
printf '%s\n' "var=$var"
$ bash < f
pos: 29
B
var=echo A
$ dash < f
pos: 85
A
B
var=
Run Code Online (Sandbox Code Playgroud)
如您所见,在grep命令运行时,stdin 中的位置在文件末尾dash,并且紧跟grep在bash.
该echo A命令是由运行dash,但在的情况下bash,它的作为输入提供给read。
发生的事情是在运行命令之前一次读取一行dash读取整个输入(实际上是一个文本块)bash。
要做到这一点,bash需要一次读取一个字节以确保它不会读取换行符,但是当输入是一个常规文件时(就像我f上面的文件一样,但也适用于 here-documents bash 实现为临时文件,而dash使用管道),bash通过按块读取并返回到行尾来优化它,您可以strace在 Linux 上看到:
$ strace -e 读,lseek bash < f
[...]
lseek(0, 0, SEEK_CUR) = 0
读取(0, "grep pos /proc/self/fdinfo/0\nIFS"..., 85 ) = 85
lseek(0, -56, SEEK_CUR) = 29
位置:29
[...]
$ strace -e read,lseek dash < f
读(0, “grep的POS / PROC /自/ fdinfo / 0 \ NIFS” ......,8192)= 85
位置:85
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12422, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
读(0,“”,1)= 0
[...]
当 stdin 是终端设备时,每个都read()返回终端发送的行,因此您通常会在bash和 中看到类似的行为dash。
在你的情况下,你可以这样做:
sudo dash << 'end-of-script'
su test <<"end"
whoami
end
end-of-script
Run Code Online (Sandbox Code Playgroud)
或更好:
sudo sh -c '
su test -c whoami
'
Run Code Online (Sandbox Code Playgroud)
甚至更好:
sudo -u test whoami
Run Code Online (Sandbox Code Playgroud)
Bash 和 Dash 处理来自 stdin 的输入的方式有所不同:启动命令时(su此处)Bash 小心地将读取位置留在导致命令运行的行之后的 stdin 上,或者通过一次读取一个字节管道,或者通过查找输入是否来自文件。Dash 不在乎,它只是读取完整的块。
这很重要,因为标准输入和其中的读取位置在 shell 和子进程之间共享。
因此,在 Bash 中,它会读取su test\nwhoami\n、 返回到第一个换行符之后,然后启动su,现在可以看到whoami\n输入。
使用 Dash,它读取su test\nwhoami\n、launchs su,它看不到任何输入和退出,然后 Dash 启动whoami。
你可以在这里看到同样的事情,在 Dash 中,date命令被执行并read得到一个空输入,而在 Bash 中,该行是readto x。
$ cat test.sh
read x
date
echo "variable x = '$x'"
$ cat test.sh | bash
variable x = 'date'
$ cat test.sh | dash
Sat Aug 24 12:25:23 EEST 2019
variable x = ''
Run Code Online (Sandbox Code Playgroud)
如果我正确解释了POSIX 描述,sh则需要 Bash 的谨慎行为,而 Dash 的松散方法不符合它:
STDIN
[...] 当 shell 使用标准输入并且它调用一个也使用标准输入的命令时,shell 应确保标准输入文件指针直接指向它在命令开始执行时读取的命令之后。它不应以这样一种方式预读,即任何旨在被调用的命令读取的字符都被 shell 消耗掉了
就其价值而言,Busybox 的 shell 在这里的行为类似于 Dash,我测试的所有其他 shell 都执行 Bash 的操作。