为什么第一个单词会丢失?

时间:2016-04-19 01:07:57

标签: bash pipe

运行以下命令给了意外(对我而言)结果。

(echo -n foo; sleep 4; echo bar) | while :; do read -t2 r; echo "###$r@@@"; done
###@@@
###@@@
###bar@@@
###@@@
...

我想知道“foo”去了哪里。我希望第一个read调用作为部分行读取。我期待以下内容:

(echo -n foo; sleep 4; echo bar) | while :; do read -t2 r; echo "###$r@@@"; done
###foo@@@
###@@@
###bar@@@
###@@@
...

由于显然这种行为(或它的文档)已经“最近”发生了变化,这是我的bash版本。

bash --version
GNU bash, version 4.3.42(1)-release (x86_64-unknown-linux-gnu)

4 个答案:

答案 0 :(得分:0)

-t选项的文档说:

  

如果在超时秒内未读取完整的输入行,则read超时并返回失败。

由于foo没有后续换行符,因此前两个read无法读取完整的输入行,因此它们会超时并返回失败。它不会将部分输入分配给$r,只是丢弃它。

第三个read成功,因为bar后跟换行符,因此读取为完整的一行。

上述内容来自4.3之前的bash版本。您所写的内容似乎应该在4.3+中工作,我不确定为什么它仍然像旧版本一样。

答案 1 :(得分:0)

让我们分析你的命令:

回声

回声部分,只输出foobar。
恰好foo在与bar不同的时间出现了:

$ (echo -n foo; sleep 1; echo bar)
foobar
$ (echo -n foo; sleep 1; echo bar) | cat
foobar

读取

读取部分只是在变量中获取该输出:

$ (echo -n foo; sleep 1; echo bar) | { read -t2 r; echo "###$r@@@"; }
###foobar@@@

如果睡眠时间大于读取超时时间,则读取任何内容:

$ (echo -n foo; sleep 4; echo bar) | { read -t2 r; echo "###$r@@@"; }
###@@@

虽然(很多行)。

有一段时间,重复阅读。一些读取得到所有输入,然后,在管道关闭后,read立即获得EOF(当发生这种情况时,它不会等待超时)。发生这种情况时会返回错误,但没有检查读取是否有错误,因此循环重复。

所以:创建了很多行:

$ (echo -n foo; sleep 1; echo bar) |
{ while :; do read -t2 r; echo "###$r@@@"; done; }

我们可以将输出重定向到文件(testfile.txt),并且我们可以控制它的使用时间(为了便于阅读,分为三行)(>不是命令的一部分) :

$ { echo -n foo; sleep 1; echo bar; } | { 
> while :; do read -t2 r; echo "###$r@@@"; \
> done; } > testfile.txt & sleep 2; kill $!
[1] 28541
[1]+  Terminated  ....

现在我们可以看到写了多少行:

$ wc -l testfile.txt
35543 testfile.txt

这意味着每秒大约有35k行写入文件 最后一次睡眠之后(杀戮之前)的数字必须大于第一次睡眠时的数字,否则不会将任何行写入文件。

哪一行:

其中哪些重复###@@@

$ grep -vn "^###@@@"
1:###foobar@@@

因此,只有文件的第一行实际包含信息,其余的是###@@@的连续重复。

睡眠时间小于读取-t

如果睡眠时间(1)低于读取超时(2),则会发生这种情况。睡眠为1,阅读为2。

睡眠时间超过读取-t

如果我们让睡眠时间长于读取超时时间:

$ a=3; b=2; c=5
$ { echo -n foo; sleep $a; echo bar; } | { 
> while :; do read -t$b r; echo "###$r@@@"; \
> done; } > testfile.txt & sleep $c; kill $!
[1] 29032
[1]+  Terminated ........

$ wc -l testfile.txt
65226 testfile.txt                      ### Again ~ 30 k per second.
$ grep -vn "^###@@@" testfile.txt
2:###bar@@@

结论

似乎正在发生的事情是,在回显写出输出之后,while会不断重复读取接收“空”输入,并将其写入文件(尽可能多的### @@@行)。

只有当第一个回声睡眠时间更长(a = 3)时,第一个读取超时(b = 2)才会在foobar的两个不同行中得到除法。

这似乎说foo在0时出现,第一次读取超时并没有收到完整的行并发出初始###@@@(吃foo没有输出?)。然后bar带有第二次读取的结束换行符,此读取将发出###bar@@@。由于管道的左侧已经终止,因此read会不断产生空行。

如果睡眠为4且超时为2,(根据您的问题),超时可能会发生两次,并且可能会向输出写入两行。这将使bar行成为第三个(两个超时,接下来是条形线),但这不是保证,因为两个睡眠正在控制它,它们可能会稍微徘徊。

从管道左侧输出所有输出后。读取接收许多“空行”???哪个写入文件,我们就可以分析它们了。

一些松散的问题。

静态(没有时间)读取。

我们可以制定一个更具体的命令来分析:

$ { echo 'foo'; sleep 3; echo 'bar'; } |
  { read -t1 r; echo "read r value=|$r|"; \
    read -t1 s; echo "read s value=|$s|"; }
read r value=|foo|
read s value=||

如果sleep(3)比读取长,则只读取第一个foo

$ { echo 'foo'; sleep 1; echo 'bar'; } |
  { read -t2 r; echo "read r value=|$r|"; \
    read -t2 s; echo "read s value=|$s|"; }
read r value=|foo|
read s value=|bar|

如果输入显示的读取超时允许时间较长,则foobar都会被读取。

虽然

如果我们包含一个简单的时间:

{ echo -n foo; sleep 4; echo bar; } | {
    while read -t2 r; do
    echo "###$r@@@";
done;
}

只有睡眠低于读取超时时才会输出:完全foobar。 如果睡眠时间长于读取超时时间:第一次读取失败,退出代码为1,退出时间为。

这就是我们无限期的原因。但是在代码中需要一个文件和一个最终超时:

a=3; b=2; c=5
{ echo -n foo; sleep $a; echo bar; } | {
    while :; do
    read -t$b r;
    echo "###$r@@@";
done;
} > testfile.txt & sleep $c; kill $!

wc -l testfile.txt; grep -vn "^###@@@" testfile.txt

答案 2 :(得分:-1)

如果您取出-t 2,则会看到它捕获foobar。这会给你足够的暗示吗?

你已选择回显'foo'而不返回该行的结尾,但read正在等待CR触发它。 foo不会发生这种情况所以读取只需要两次等待获取任何内容。然后当'bar \ n'被回显时,它确实包含一个CR,所以read会选择它。

<强>更新 我一直在查看旧版bash的文档。最初的问题是更新后包含bash版本。如上所述,似乎bash文档已经更改,但实际行为没有,并且可能涉及bash bug。

4.3之前的文档表明,在超时时,将引发错误,但不表示是否有任何缓冲输入将被读入下一个变量。 4.3文档说“如果读取超时,读取将任何部分输入读取保存到指定的变量名称。”

根据这些新信息,我对此主题的许多评论都不正确。

答案 3 :(得分:-4)

尝试

... | while read a
do
    ...
done

我想也许虚拟命令':'接受输入。