为什么父 shell here-document 不适用于 dash 中的子命令,但 bash 可以工作?

为什么父 shell here-document 不适用于 dash 中的子命令,但 bash 可以工作?

我在 debianstretch 上运行,以下所有命令(dashbash)都输入到 bash 中。
似乎whoami从未以用户身份运行测试在破折号中,如下面的代码所示。

$ sudo dash << 'end'
> su test
> whoami
> end
root
$ sudo bash << 'end'
> su test
> whoami
> end
test

答案1

请考虑这个例子:

$ 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=

正如您所看到的,运行命令时grep,stdin 中的位置位于文件末尾,并且紧接在 中命令dash后面的换行符之后。grepbash

echo A命令由 运行,dash但在 的情况下bash,它作为输入提供给read

发生的情况是,在运行命令之前一次读取一行,dash读取整个输入(实际上是一段文本) 。bash

为此,bash需要一次读取一个字节,以确保它不会读取超过换行符的内容,但是当输入是常规文件时(就像f上面我的文件的情况一样,而且对于此处文档也适用) bash 作为临时文件实现,同时dash使用管道),bash通过按块读取并回溯到行尾来优化它,您可以在straceLinux 上看到:

$ 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 读取,lseek 破折号 < f
读(0, "grep pos /proc/self/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

或更好:

sudo sh -c '
  su test -c whoami
'

甚至更好:

sudo -u test whoami

答案2

Bash 和 Dash 处理来自 stdin 的输入的方式有所不同:当启动命令时(su此处),Bash 会小心地将读取位置保留在导致命令运行的行之后的 stdin 上,方法是一次读取一个字节管道,或者通过向后查找输入是否来自文件。达世币不在乎,它只是读取完整的区块。

这很重要,因为标准输入和其中的读取位置在 shell 和子进程之间共享。

因此,使用 Bash 时,它会读取su test\nwhoami\n,返回到第一个换行符之后,然后启动su,现在可以whoami\n在输入中看到。

对于 Dash,它会读取su test\nwhoami\n、启动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 = ''

如果我解释POSIX 描述sh正确的是,Bash 的谨慎行为是必需的,而 Dash 的宽松方法并不符合它:

STDIN
[...] 当 shell 使用标准输入并且调用也使用标准输入的命令时,shell 应确保在命令开始执行时标准输入文件指针直接指向它所读取的命令之后。它不应以这样的方式预读:任何要由调用的命令读取的字符都会被 shell 消耗

就其价值而言,Busybox 的 shell 的行为与 Dash 类似,我测试的所有其他 shell 都执行 Bash 的操作。

相关内容