我已经在 bash 脚本中通过管道传输了一行,并想在将其提供给程序之前检查管道是否有数据。
我搜索了一下,test -t 0
但在这里不起作用。总是返回 false。那么如何确定管道有数据呢?
例子:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
输出:fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
输出:fill
不像标准/规范的方法来测试上述管道是否产生输出?需要保留输入以将其传递给程序。这概括了如何将输出从一个进程传输到另一个进程,但仅在第一个进程有输出时才执行?其重点是发送电子邮件。
答案1
无法使用常用的 shell 实用程序来查看管道的内容,也没有可移植的方法从管道中读取字符然后将其放回。知道管道有数据的唯一方法是读取一个字节,然后必须将该字节发送到其目的地。
所以就这样做:读取一个字节;如果检测到文件结尾,则在输入为空时执行您想要执行的操作;如果您确实读取了一个字节,则在输入不为空时分叉您想要执行的操作,将该字节通过管道传输到其中,然后通过管道传输其余数据。
first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-7)
if [ -z "$first_byte" ]; then
# stuff to do if the input is empty
else
{
printf "\\$first_byte"
cat
} | {
# stuff to do if the input is not empty
}
fi
这ifne
效用来自Joey Hess 的 moreutils如果输入不为空,则运行命令。默认情况下通常不会安装它,但它应该可以在大多数 UNIX 变体上使用或轻松构建。如果输入为空,ifne
则不执行任何操作并返回状态0,无法与命令运行成功区分开。如果你想在输入为空时执行某些操作,则需要安排命令不返回 0,这可以通过让成功案例返回可区分的错误状态来完成:
ifne sh -c 'do_stuff_with_input && exit 254'
case $? in
0) echo empty;;
254) echo success;;
*) echo failure;;
esac
test -t 0
与此无关;它测试标准输入是否是终端。它没有以任何方式说明任何输入是否可用。
这里任意选择 254,避免使用 255,因为执行某些命令exit(-1)
(通常会导致$?
255)相对常见(-exec(-2)
不太常见)。
答案2
一个简单的解决方案是使用ifne
命令(如果输入不为空)。在某些发行版中,默认情况下不安装它。它是大多数发行版中软件包的一部分moreutils
。
ifne
当且仅当标准输入不为空时运行给定命令
请注意,如果标准输入不为空,则会传递ifne
给给定的命令
答案3
检查 stdin (0) 的文件描述符是打开还是关闭:
[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"
答案4
老问题,但万一有人像我一样遇到它:我的解决方案是超时阅读。
while read -t 5 line; do
echo "$line"
done
如果stdin
为空,则 5 秒后返回。否则它将读取所有输入,您可以根据需要对其进行处理。
在这些受支持的情况下-t
,您可以在使用 读取任何数据之前测试输入-t0
。 (还指定-u0
STDIN,因为 awslinux 需要它,而 ubuntu 则假定它)
if [ $(read -u0 -t0) ]; then ....
之后你就可以开始正常阅读了
从help read
:
如果 TIMEOUT 为 0,则 read 立即返回,而不尝试读取任何数据,仅当指定文件描述符上的输入可用时才返回成功。