在读取脚本时,shell 将从文件、管道或可能的其他源(stdin?)读取脚本。在某些角落条件下可能无法查找输入(无法将文件位置倒回到先前位置)。
据说read 一次读取一个字节的 stdin,直到找到未转义的换行符
shell 是否也应该从其脚本输入中一次读取一个字符?
我的意思是脚本,而不是可以使用的附加数据文本文件。
如果是这样:为什么需要这样做?它在某些规范中定义了吗?
所有 shell 的工作方式都相似吗?哪个不是?
答案1
shell将从脚本文件或设备描述符中读取
或者从管道,这可能是获取不可查找的输入 fd 的最简单方法。
shell 是否也应该从其脚本输入中一次读取一个字符?
如果它想要支持运行从 stdin 读取的命令的脚本,并希望使用脚本本身的行获取输入。
就像这样:
$ cat foo.sh
#!/bin/sh
line | sed -e 's/^/* /'
xxx
echo "end."
$ cat foo.sh | bash
* xxx
end.
该line
命令从标准输入 ( ) 行读取一行xxx
,而 shell 将其他行作为命令读取。为此,line
还需要注意不要读取输入太多,否则 shell 将看不到以下行。使用 GNU 实用程序,head -n1
会读取太多内容,例如sed
. util-linux 中的实用line
程序会注意一次读取一个字节,以免读取超过换行符。
上面的脚本不适用于 eg dash
,因为它一次读取脚本的整个块:
$ cat foo.sh | dash
*
dash: 3: xxx: not found
end.
Dash 和 Busybox 读取完整块,我测试的其他(Bash、Ksh 和mksh
Zsh)则逐字节读取。
请注意,这是一个相当复杂的脚本,如果运行为 例如 ,则它无法正常工作bash foo.sh
,因为在这种情况下stdin
不会指向脚本本身,并且该xxx
行将被视为命令。如果想要在脚本本身中包含数据,那么使用此处文档可能会更好。当运行为sh bar.sh
,sh < bar.sh
或 时,这适用于任何 shell cat bar.sh | sh
:
$ cat bar.sh
#!/bin/sh
sed -e 's/^/* /' <<EOF
xxx
EOF
echo "end."
答案2
对于 POSIX 兼容的 shell 来说是的。 bash 开发者是这样说的:
POSIX 需要从 stdin 读取的脚本。当从作为参数给出的脚本读取时,bash 读取块。
而且,确实,POSIX 规范说这个(强调我的):
当 shell 使用标准输入并且调用也使用标准输入的命令时,外壳应确保标准输入文件指针直接指向它所读取的命令之后当命令开始执行时。不得预读以这样的方式任何字符打算由调用的命令读取的字符会被 shell 使用(无论是否由 shell 解释),或者 shell 不会看到调用的命令未读取的字符。
即:(对于标准输入脚本)shell 应一次读取一个字符。
在 C 语言环境中,一个字符就是一个字节。
看来 posh、mksh、lksh、attsh、yash、ksh、zsh 和 bash 符合这个要求。
然而 ash (busybox sh) 和 dash 则不然。