如何检查管道是否为空并对数据运行命令(如果不是)?

zet*_*tah 73 shell bash pipe

我在 bash 脚本中输入了一行,并想在将它提供给程序之前检查管道是否有数据。

搜索我找到了,test -t 0但在这里不起作用。总是返回假。那么如何确定管道有数据呢?

例子:

echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Run Code Online (Sandbox Code Playgroud)

输出: fill

echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Run Code Online (Sandbox Code Playgroud)

输出: fill

测试上述管道是否产生输出的标准/规范方式不同需要保留输入以将其传递给程序。这概括了如何将输出从一个进程传送到另一个进程,但仅在第一个进程有输出时才执行?专注于发送电子邮件。

Gil*_*il' 57

无法使用常用的 shell 实用程序查看管道的内容,也无法从管道中读取字符然后将其放回原处。知道管道有数据的唯一方法是读取一个字节,然后您必须将该字节送到其目的地。

所以这样做:读取一个字节;如果检测到文件结束,则在输入为空时执行您想做的操作;如果您确实读取了一个字节,则在输入不为空时分叉您想要执行的操作,将该字节输送到其中,然后输送其余数据。

first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-9)
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
Run Code Online (Sandbox Code Playgroud)

ifne从实用乔伊赫斯的moreutils如果输入不为空运行的命令。默认情况下通常不会安装它,但它应该可用或易于在大多数 unix 变体上构建。如果输入为空,ifne则不执行任何操作并返回状态 0,这与命令运行成功无法区分。如果你想在输入为空的情况下做一些事情,你需要安排命令不返回0,这可以通过让成功案例返回一个可区分的错误状态来完成:

ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
  0) echo empty;;
  255) echo success;;
  *) echo failure;;
esac
Run Code Online (Sandbox Code Playgroud)

test -t 0与此无关;它测试标准输入是否是终端。它没有以任何方式说明是否有任何输入可用。


小智 24

一个简单的解决方案是使用ifne命令(如果输入不为空)。在某些发行版中,默认情况下不安装它。它是moreutils大多数发行版中软件包的一部分。

ifne 当且仅当标准输入不为空时运行给定的命令

请注意,如果标准输入不为空,则将其传递ifne给给定的命令

  • 截至 2017 年,它在 Mac 或 Ubuntu 中默认不存在。 (3认同)

mvi*_*eck 19

检查 stdin (0) 的文件描述符是打开还是关闭:

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"
Run Code Online (Sandbox Code Playgroud)

  • `[ -t 0 ]` 检查 fd 0 是否对 **tty** 打开,而不是它是关闭还是打开。 (12认同)
  • @JepZ 嗯?`./that_script </dev/null` => “标准输入有数据”。或者`./that_script <&-` 让标准输入真正*关闭*。 (2认同)

yas*_*hma 14

如果你喜欢简短而神秘的单线:

$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty
Run Code Online (Sandbox Code Playgroud)

我使用了原始问题中的示例。如果您不希望使用-q带有 grep的管道数据使用选项

  • `grep .` 正是我正在寻找的,而且非常简单。谢谢! (2认同)

小智 10

老问题,但如果有人像我一样遇到它:我的解决方案是超时阅读。

while read -t 5 line; do
    echo "$line"
done
Run Code Online (Sandbox Code Playgroud)

如果stdin为空,这将在 5 秒后返回。否则,它将读取所有输入,您可以根据需要对其进行处理。

  • 虽然我喜欢这个想法,但遗憾的是 `-t` 不是 POSIX 的一部分:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html (2认同)

mos*_*svy 8

检查 Unix 中是否有可供读取的数据的一种简单方法是使用FIONREADioctl。

我想不出有任何标准实用程序可以做到这一点,所以这里有一个简单的程序(比ifne来自 moreutils 恕我直言 ;-)更好)。

fionread [ prog args ... ]
Run Code Online (Sandbox Code Playgroud)

如果 stdin 上没有可用数据,它将以状态 1 退出。如果有数据,它将运行prog。如果没有prog给出,它将以状态 0 退出。

这应该适用于大多数类型的文件描述符,而不仅仅是管道。

fionread.c

fionread [ prog args ... ]
Run Code Online (Sandbox Code Playgroud)

  • @DerekMahar 因为 a) 它小得多 b) 快得多 c) 它是一个进程,它直接执行命令。 (2认同)
  • @DerekMahar d) 它不会改变传递给 `cmd` 的 stdin 的 _nature_;例如。如果 stdin 是一个套接字,`cmd` 仍然可以在它上面调用 `getpeername(2)` 来查看谁在连接的末尾。正如 Stéphane Chazelas 所提到的,您可以在 perl、python、ruby 等中轻松完成此操作。不过,不在 shell 中或使用任何标准实用程序。 (2认同)

ImH*_*ere 5

在 bash 中:

read -t 0 
Run Code Online (Sandbox Code Playgroud)

检测输入是否有数据(不读取任何内容)。然后您可以读取输入(如果在执行读取时输入可用):

if     read -t 0
then   read -r input
       echo "got input: $input"
else   echo "No data to read"
fi
Run Code Online (Sandbox Code Playgroud)

注意:理解这取决于时间。这会检测输入是否仅在read -t运行时已有数据。

例如,与

{ sleep 0.1; echo "abc"; } | read -t 0; echo "$?"
Run Code Online (Sandbox Code Playgroud)

输出是1(读取失败,即:空输入)。echo 写入了一些数据,但启动和写入第一个字节的速度不是很快,因此,read -t 0将报告其输入为空,因为程序尚未写入任何内容。

  • @PavelPatrin [那不起作用](https://unix.stackexchange.com/questions/33049/how-to-check-if-a-pipe-is-empty-and-run-a-command-on-the -data-if-it-isnt/498065?noredirect=1#comment916641_497121)。从您的链接中可以清楚地看出,`bash` 将执行 `select()` 或 `ioctl(FIONREAD)`,或者两者都不执行,但不能同时执行,因为它应该起作用。`read -t0` 坏了。[请勿使用](https://unix.stackexchange.com/questions/33049/how-to-check-if-a-pipe-is-empty-and-run-a-command-on-the-data -if-it-isnt/498065?noredirect=1#comment916652_497121) (3认同)