运行以下命令给了意外(对我而言)结果。
(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)
答案 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@@@
因此,只有文件的第一行实际包含信息,其余的是###@@@
的连续重复。
如果睡眠时间(1)低于读取超时(2),则会发生这种情况。睡眠为1,阅读为2。
如果我们让睡眠时间长于读取超时时间:
$ 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)才会在foo
和bar
的两个不同行中得到除法。
这似乎说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|
如果输入显示的读取超时允许时间较长,则foo
和bar
都会被读取。
如果我们包含一个简单的时间:
{ 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
我想也许虚拟命令':'接受输入。