shell 如何保留命令文件输入行边界?

How does shell preserve command file input line boundary?

SUSv4 2016版的sh的STDIN描述说

It shall not read ahead in such a manner that any characters intended to be read by the invoked command are consumed by the shell

我做了一个实验,通过将以下脚本文件重定向到 sh -s

#!/bin/sh
./rewind # a c program that reads up its stdin, prints it to stdout, before SEEK_SET its standard input to 0. 
echo 1234-1234

它一直在打印 "echo 1234-1234"。 (如果 shell 消耗了整个文件块,它只会打印出“1234-1234”)

很明显,shells(至少我的)确实在行边界读取。

但是,当我检查 FreeBSD ash "input.c" 源代码时,它读取 BUFSIZ 字节块,我不明白它如何保留行边界。

我想知道的是:当 shells 的源代码明显显示它们是按块读取时,它们如何保留行边界?

标准输入在某些情况下是不可搜索的,例如,如果它是从管道或终端重定向的。例如。有一个名为 rew 的文件,其内容为:

#!/bin/bash
echo 123
perl -E 'seek(STDIN,0,0) or die "$!"' #the rewind

并将其用作

bash -s < rew

打印

123
123
...
123
^C

所以,当 STDIN 是 seekable 时,它将按预期工作,但从管道中尝试相同,例如:

cat rew | bash -s   #the cat is intentional here :)

将打印

123
Illegal seek at -e line 1.

因此,当您的 C 程序 rewind 试图在不可搜索的输入中搜索时,它应该打印错误。

您可以使用以下脚本演示这种(令人惊讶的)行为:

$ cat test.sh
cut -f 1
printf '%s\t%s\n' script file

如果将它传递给 shell 的标准输入,第 2 行以后应该成为 cut 命令的标准输入:

$ sh < test.sh
printf '%s\t%s\n' script file

如果你改为 运行 它正常并按字面输入 foo,然后是 Tabbar EnterCtrl-d,您应该看到标准输入正常链接:

$ sh test.sh 
foo     bar
foo
script  file

注释:

$ sh test.sh # Your command
foo     bar # Your input to `cut`
foo # The output of `cut`
script  file # The output of `printf`

当我在 "shell.h" 中使用定义为 1 的 NO_HISTORY 宏编译 FreeBSD ash 时,它会消耗整个文件并在我的倒带测试程序中仅输出 1234-1234。显然,FreeBSD ash 依赖于 libedit 来保留 IO 行边界。