为什么 awk 在从管道读取时会进行完全缓冲

为什么 awk 在从管道读取时会进行完全缓冲

我正在从连接到发送 nmea 字符串的 GPS 设备的串行端口读取数据。

一个简化的调用来说明我的观点:

  $ awk '{ print $0 }' /dev/ttyPSC9 
  GPGGA,073651.000,6310.1043,N,01436.1539,E,1,07,1.0,340.2,M,33.3,M,,0000*56
  $GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39
  $GPRMC,073651.000,A,6310.1043,N,01436.1539,E,0.42,163.42,070312,,,A*67
  GPGGA,073652.000,6310.1043,N,01436.1540,E,1,07,1.0,339.2,M,33.3,M,,0000*55
  $GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39

如果我尝试从管道读取,awk 会在将输入发送到 stdout 之前缓冲输入。

$ cat /dev/ttyPSC9 | awk '{ print $0 }'
<long pause>
GPGGA,073651.000,6310.1043,N,01436.1539,E,1,07,1.0,340.2,M,33.3,M,,0000*56
$GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39
$GPRMC,073651.000,A,6310.1043,N,01436.1539,E,0.42,163.42,070312,,,A*67
GPGGA,073652.000,6310.1043,N,01436.1540,E,1,07,1.0,339.2,M,33.3,M,,0000*55
$GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39

如何避免缓冲?

编辑:凯尔琼斯建议 cat 正在缓冲其输出,但这似乎没有发生:

$ strace cat /dev/ttyPSC9 | awk '{ print $0 }'
write(1, "2,"..., 2)                    = 2
read(3, "E"..., 4096)                   = 1
write(1, "E"..., 1)                     = 1
read(3, ",0"..., 4096)                  = 2

当我思考时:我认为程序在写入终端时使用行缓冲,并在所有其他情况下使用“常规缓冲”。那么,为什么 cat 不进行更多缓冲呢?串口信号是EOF吗?那为什么cat没有被终止呢?

我的awk是mawk 1.2。

答案1

我知道这是一个老问题,但是一句话可能会帮助那些来这里寻找的人:

cat /dev/ttyPSC9 | awk '{ print $0; system("")}'

system("")确实有效,并且符合 POSIX 标准。非 posix 系统:要小心。

存在一个更具体的函数fflush()可以执行相同的操作,但在旧版本的 awk 中不可用。

一条重要信息来自文档关于使用system("")

gawk 将 system() 函数的这种使用视为一种特殊情况,并且足够聪明,不会使用空命令运行 shell(或其他命令解释器)。因此,对于gawk来说,这个惯用语不仅有用,而且高效。

答案2

它可能是在 awk 中缓冲,而不是在 cat 中。在第一种情况下,awk 认为它是交互式的,因为它的输入和输出是 TTY(即使它们是不同的 TTY - 我猜 awk 没有检查这一点)。在第二个中,输入是管道,因此它以非交互方式运行。

您需要在 awk 程序中显式刷新。但这不是便携式的。

有关如何刷新输出的更多背景和详细信息,请阅读:http://www.gnu.org/software/gawk/manual/html_node/I_002fO-Functions.html

答案3

老话题,但也许值得添加一个解决方案,以透明的方式改变流缓冲行为,而不需要一些魔法system(""):-)

cat /dev/ttyPSC9 | stdbuf --output=L awk '{print $0}'

最近我自己用它来捕捉 D-Bus 事件。

gdbus monitor -y -d org.freedesktop.login1 | stdbuf -oL  grep LockedHint

相关内容