尝试读取空的 STDIN 时 Cat 挂起

尝试读取空的 STDIN 时 Cat 挂起

我的脚本尝试收集执行时 STDIN 中可能存在或不存在的信息,但如果管道为空,cat 会挂起。如果 STDIN 中没有任何内容,我如何确保我的脚本跳过此步骤?

stdin=$(cat <&0)

请注意,我特别不是在寻找任何引用 /dev/ 的解决方案,因为我希望无论 /dev/ 是否已挂载(如果可能)都可以在 chroot 中使用它。

答案1

通常,在使用管道和标准输入时,耗尽的管道没有特殊含义。新数据仍可能出现,直到出现eof关闭管道的命令。您的命令cat按预期终止于eof。如果之前没有数据eof,那么您就可以说标准输入曾是确实空了。

考虑一下。比 慢 (很多) 的sender | receiver情况并不少见;在这种情况下,的标准输入几乎总是耗尽,但您几乎从不想因此而终止整个管道。因此,在“空”(耗尽但尚未终止) 标准输入时退出的工具是例外,而不是标准。senderreceiverreceiver

在 Bash 中有read -t 0-tPOSIX 不要求)。来自help read

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

默认情况下read从 stdin 读取,因此退出状态read -t 0将告诉您 stdin 是否为“空”。但要小心!类似这样的命令

echo 1 | read -t 0

可能会成功退出,也可能会失败,因为echoread同时运行,而不是顺序运行。为了避免这种情况,你的脚本应该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 秒内既没有字节也没有eofwc将只接收eof;线路不会停滞。

相关内容