我的脚本尝试收集执行时 STDIN 中可能存在或不存在的信息,但如果管道为空,cat 会挂起。如果 STDIN 中没有任何内容,我如何确保我的脚本跳过此步骤?
stdin=$(cat <&0)
请注意,我特别不是在寻找任何引用 /dev/ 的解决方案,因为我希望无论 /dev/ 是否已挂载(如果可能)都可以在 chroot 中使用它。
答案1
通常,在使用管道和标准输入时,耗尽的管道没有特殊含义。新数据仍可能出现,直到出现eof
关闭管道的命令。您的命令cat
按预期终止于eof
。如果之前没有数据eof
,那么您就可以说标准输入曾是确实空了。
考虑一下。比 慢 (很多) 的sender | receiver
情况并不少见;在这种情况下,的标准输入几乎总是耗尽,但您几乎从不想因此而终止整个管道。因此,在“空”(耗尽但尚未终止) 标准输入时退出的工具是例外,而不是标准。sender
receiver
receiver
在 Bash 中有read -t 0
(-t
POSIX 不要求)。来自help read
:
如果
TIMEOUT
是0
,read
则立即返回,而不尝试读取任何数据,仅当指定的文件描述符上有可用输入时才返回成功。
默认情况下read
从 stdin 读取,因此退出状态read -t 0
将告诉您 stdin 是否为“空”。但要小心!类似这样的命令
echo 1 | read -t 0
可能会成功退出,也可能会失败,因为echo
和read
同时运行,而不是顺序运行。为了避免这种情况,你的脚本应该sleep
在 之前运行一段时间read -t 0
。根据 stdin 的来源,“一段时间”可能相对较长。请执行以下操作:
sleep 1
if read -t 0; then … # process stdin here, you know it's non-empty
你用从 stdin 获取的数据填充一个变量。由于将二进制数据存储在变量中不是一个好主意(请阅读这),也许你的数据只是文本。如果是这样,请read -t
像这样使用:
read -r -t 5 -d $'\0' stdin
空字符(无论如何您都无法将其存储在 Bash 变量中)作为分隔符 ( -d $'\0'
) 将允许您将任何文本(例如换行符)读入stdin
变量。最多 5 秒后 ( -t 5
) 命令终止,允许您的脚本继续运行。
另一种方法是使用timeout
。我的 Debian 中的一个基本示例:
timeout --foreground 5 cat | wc -c
(wc -c
用解析 stdin 的代码替换;这只是一个例子)。
这应该可以很好地处理二进制数据。如果cat
没有得到,eof
那么 5 秒后它将被终止,所以无论如何wc
都会得到eof
,并且线路不会停滞。问题是cat
无论它是否正在处理任何数据,它都会被终止。我想你想得到所有的数据,只要有一些,即使需要超过 5 秒。改进版本:
{ timeout --foreground 5 dd bs=1 count=1 2>/dev/null && cat; } | wc -c
如果第一个字节在 5 秒内出现,cat
将被触发。然后它将处理任何进一步的输入,直到eof
,无论需要多长时间。包括第一个字节(如果有)在内的所有内容将转到wc
。如果 5 秒内既没有字节也没有eof
,wc
将只接收eof
;线路不会停滞。