更新

更新

我的脚本(应该)表现不同,具体取决于输入流中数据的存在。所以我可以这样调用它:

$ my-script.sh

或者:

$ my-script.sh <<-MARK
    Data comes...
    ...data goes.
MARK

或者:

$ some-command | my-script.sh

最后两个案例应该读取数据,而第一个案例应该注意到数据丢失并采取相应的行动。

该脚本的关键部分(摘录)是:

#!/bin/bash
local myData;
read -d '' -t 0 myData;

[ -z "${myData}" ] && {
    # Notice the lack of the data.
} || {
    # Process the data.
}

我用来read读取输入数据,然后选择-d ''读取多行(这是预期的),并将-t 0超时设置为零。为什么超时?根据help read(打字保持不变;粗体是我的):

-t timeout如果在几秒钟内未读取完整的输入行,则超时并返回失败TIMEOUT。该变量的值TMOUT 是默认超时。 TIMEOUT可能是小数。 如果TIMEOUT为 0,则仅当指定文件描述符上的输入可用时,读取才返回成功。如果超过超时则退出状态大于128

因此,据我了解,在情况 2 和 3 中,它应该立即读取数据。不幸的是事实并非如此。由于-t可以采用小数值(根据上面的手册页),将读取行更改为:

read -d '' -t 0.01 myData;

当数据存在时实际读取数据,如果不存在则跳过它(10ms 超时后)。但当TIMEOUT设置为 real时它也应该起作用0

为什么实际上没有?如何解决这个问题?也许有其他解决方案来解决“根据数据的存在而采取不同的行动”的问题吗?

更新

感谢@Isaac,我发现引用的在线版本和我的本地版本之间存在误导性差异(通常我没有将语言环境设置为 en_US,所以help read给了我翻译,但我无法在此处粘贴,并查找在线翻译比设置新环境更快——但这导致了整个问题)。

因此对于 4.4.12 版本的 Bash 来说:

如果TIMEOUT为0,read立即返回,而不尝试读取任何数据,仅当输入在指定的文件描述符上可用时才返回成功。

这给人的印象与“如果 TIMEOUT 为 0,则仅当输入在指定的文件描述符上可用时读取才返回成功”——对我来说,这实际上意味着尝试读取数据。

所以最后我测试了这个并且它工作得很好:

read -t 0 && read -d '' myData;

意思是:看看有没有什么可读的,如果成功了,就读吧。

对于基本问题,艾萨克提供了正确的答案。至于替代解决方案,我更喜欢上面的“read && read”方法。

答案1

不,read -t 0不是读取任何数据。

您正在阅读错误的手册。将man read给出 PATH 中名为 的程序的手册read。这不是手册内置重击read

要阅读 bash 手册页,请使用man bash并搜索read [-ers]或简单地使用:

help read

其中包含此内容(在版本 4.4 上):

如果超时为 0,则 read 立即返回,而不尝试读取任何数据。

所以,不,不会使用 读取任何数据-t 0


  1. Q1

    为什么实际上没有?

因为这是有记录的工作方式。

  1. Q2

    如何解决这个问题?

只有当这被认为是一个错误(我怀疑它会)并且 bash 源代码被更改时。

  1. 第三季度

    也许有其他解决方案来解决“根据数据的存在而采取不同的行动”的问题吗?

是的,实际上,有一个解决方案。help read我上面引用的内容之后的下一句话是:

仅当指定文件描述符上的输入可用时才返回成功。

这意味着即使它不读取任何数据,它也可以用来触发对可用数据的实际读取:

read -t 0 && read -d '' myData

[ "$myData" ] && echo "got input" || echo "no input was available"

这样就不会耽搁了。

答案2

披露:不是一个严格的答案,很多挥手

这里有几件事在起作用。首先,bash内部处理零超时完全与非零超时不同。 (如果您决定查看代码,请准备好花一些时间在上面 - 它并不像人们想象的那么简单;它相当深入。)

其次,使用管道时存在固有的竞争条件 - 进程是“从右到左”创建的,即首先创建命令行上的最后一个进程。然而,不能保证 shell 会继续执行,在已经创建的进程自行调度之前从命令行生成进一步的进程 - 因此很可能会在read输入准备好之前执行。根据特定的系统,如果您稍微调整一下超时值,您可能可以使用-t 0类似的方法重现该行为-t 0.000011)

除此之外,考虑到当今大多数系统都是并行的(多核或多处理器)——根据内核如何调度管道中的所有进程,您将得到不同的结果(影响这一点的一件事是当前系统负载,顺便说一句)。通过将进程组限制为单个特定CPU(例如使用taskset)可以缓解此问题,但上述子进程调度问题不会消失。


1)我实际上设法达到了这样的程度,即read只收到了部分输入(3 个字符中的 1 或 2 个字符)。

相关内容