我的脚本(应该)表现不同,具体取决于输入流中数据的存在。所以我可以这样调用它:
$ 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
。
- Q1
为什么实际上没有?
因为这是有记录的工作方式。
- Q2
如何解决这个问题?
只有当这被认为是一个错误(我怀疑它会)并且 bash 源代码被更改时。
- 第三季度
也许有其他解决方案来解决“根据数据的存在而采取不同的行动”的问题吗?
是的,实际上,有一个解决方案。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.00001
。1)
除此之外,考虑到当今大多数系统都是并行的(多核或多处理器)——根据内核如何调度管道中的所有进程,您将得到不同的结果(影响这一点的一件事是当前系统负载,顺便说一句)。通过将进程组限制为单个特定CPU(例如使用taskset
)可以缓解此问题,但上述子进程调度问题不会消失。
1)我实际上设法达到了这样的程度,即read
只收到了部分输入(3 个字符中的 1 或 2 个字符)。