shell 在while循环文件读取期间,stdin的第一行在哪里丢失?

cunj1qz1  于 2023-02-24  发布在  Shell
关注(0)|答案(2)|浏览(131)

假设我们有一个文件,其中的数字1到5是逐行写的,当我打开一个文件作为标准输入并使用“while read”时,可以读取stdin的命令无法读取该文件的第一行。
$ while read x; do sed ''; done<file
2
3
4
5
使用哪个命令没有区别:sed、awk、cat等。如果命令能够从stdin读取,就会出现这个问题。我使用的shell之间也没有区别。我在sh、bash和zsh中尝试了同样的操作,结果完全相同。
值得注意的是,循环迭代了5次,每行一次,例如:
$ while read x; do printf 'something\n'; done<file
一些东西
一些东西
一些东西
一些东西
一些东西
我知道如果我想正确地读取所有行,我必须在read命令中指定一个变量,然后将其传递给命令。但是我试图弄清楚这里发生了什么。为什么当我没有直接为命令指定input时会出现这个问题?也许这是一个没有功能目的的副作用。
我找不到任何有关"while read“语句这种行为的信息,也找不到任何有类似问题的人。

1cklez4t

1cklez4t1#

您的代码只迭代 * 一次 *。

while read x; do sed ''; done<file

...行为如下:

  1. file打开并连接到标准输入
  2. read使用stdin文件的第一行,并将其放入$x
  3. sed ''从stdin中消耗 * 文件的整个剩余部分 *,并将其打印到stdout,不做任何更改。
  4. read发现没有更多的数据(因为sed已经用完了所有数据),循环结束。
    如果希望sed只对read x使用的一行进行操作,并防止其他错误,可以改为:
while IFS= read -r x; do printf '%s\n' "$x" | sed ''; done <file

变更:

  • 使用IFS=可防止read删除前导或尾随空格。
  • 使用-r参数可防止read使用反斜杠。
  • printf '%s\n' "$x"sed的管道传输改变了sed的stdin,使得它不包含 * 文件的其余部分 *,而是仅包含一行。因此,这确保了sed正在处理由read消耗的行。而不是忽略该行并处理文件的整个其余部分。(使用printf代替echo是一个正确性问题;请参见UNIX & Linux Stack Exchange上的Why is printf better than echo?)。
nwwlzxa7

nwwlzxa72#

stdin的第一行没有丢失,而是在使用重定向操作符'<'将文件内容重定向到while循环时由shell使用。第一行用作初始化while循环的输入,后续行在循环内部读取。这就是为什么循环内部的命令不处理第一行的原因。为了避免这种情况,您可以使用'<&'将文件重定向到新的文件描述符,如下所示:

$ while read x; do sed ''; done <&3 3<file

相关问题