【问题标题】:Interacting with awk while processing a pipe在处理管道时与 awk 交互
【发布时间】:2018-11-25 08:23:41
【问题描述】:

awk 通过管道处理/dev/stdin 时,有没有办法让awk 交互。

想象一下,我有一个不断生成数据的程序。示例:

$ od -vAn -tu2 -w2 < /dev/urandom
 2357
60431
19223
...    

这个数据正在由一个非常先进的awk 脚本通过管道处理:

$ od -vAn -tu2 -w2 < /dev/urandom | awk '{print}'

问题:是否有可能使这个awk 程序具有交互性:

  • 程序连续打印输出
  • 当按下单个键时(例如z),它开始为从管道中读取的每一行只输出0
  • 当再次按键时,它继续输出原始数据,显然跳过了它打印为0的已经处理过的记录。

问题:

  • /dev/stdin(也称为-)已经在使用中,所以键盘交互需要用/dev/tty接还是有别的办法?

  • getline key &lt; "/dev/tty" 一直等待,直到遇到RS,所以在默认情况下,您需要按两个键(zEnter):

    $ awk 'BEGIN{ getline key < "/dev/tty"; print key}'
    

    这是可以接受的,但我更喜欢单键。

    那么,是否可以在本地设置 RS 以使 getline 读取单个字符?这样我们就可以在本地修改RS 并在getline 之后重置它。另一种方法可能是使用 shell 函数read。但是bashzsh不兼容。

  • getline 等待输入直到时间结束。所以它基本上停止了管道的处理。有一个gawk extention 允许您设置超时,但这仅在gawk 4.2 之后可用。 所以我相信这可能会奏效:

    awk '{print p ? 0 : $0 }
         { PROCINFO["/dev/tty", "READ_TIMEOUT"]=1;
           while (getline key < "/dev/tty") p=key=="z"?!p:p
         }
    

    但是,我无法访问gawk 4.2(更新:这不起作用)

请求:

  1. 我更喜欢完全符合 POSIX 的版本,它是或完全是 awk 或使用符合 POSIX 的系统调用
  2. 如果无法做到这一点,可以使用 3.1.7 之前的 gawk 扩展和独立于 shell 的系统调用。
  3. 作为最后的手段,我会接受任何 shell-awk 构造,它可以在仅由 awk 连续读取数据的单一条件下实现这一点(所以我在这里考虑使用多个管道)。

【问题讨论】:

  • 你可以在你的while循环中运行一个外部脚本来用不可靠的bash替换getline的东西,
  • 这至少会非常棘手,并且接近“不可能使用 awk”。您使用 Gawk 4.2 的超时解决方案可能会将正常流速率限制为每个超时周期一个输出数。我没有检查超时的计时单位,但你没有提到毫秒(或微秒或纳秒),所以它可能是“整数秒”。在 C 程序中,您会安排监视 /dev/stdin/dev/tty 的“可用字符”,并在可用时打印标准输入(或零),但在 /dev/tty 有可用数据的情况下,你会读的。
  • 您还必须考虑是否要在 /dev/tty 输入上使用原始 I/O,以便可以检测到单个击键。将终端置于原始状态意味着所有使用它的程序(我相信只有这个程序)将具有原始 I/O。没有信号;键盘没有中断。要小心。 (C 程序可能会使用 select()poll() — 或者在 Linux 上可能会使用 epoll()。)
  • @LuisMuñoz read 的替代品可能是这个漂亮的技巧:$ stty cbreak -echo; KEY=$(dd if=/dev/tty bs=1 count=1 2&gt;/dev/null); stty -cbreak echo(来源 here
  • 对不起,我刚看到这个。我认为在 awk 中没有任何好的方法可以做到这一点。使用 gawk,您可能会执行一些操作,例如将 PROCINFO["-", "READ_TIMEOUT"] 设置为一个小值,然后在处理每一行实际输入后从 stdin 获取行,但我怀疑这并不像说的那么容易,并且超时的可能粒度可能在这种情况下太高而不能真正有用。

标签: bash shell awk


【解决方案1】:

经过一番搜索,我想出了一个允许这样做的 Bash 脚本。这个想法是将 unique identifiable 字符串注入到 awk 正在处理的管道中。原始程序 od 和 bash 脚本都写入管道。为了不破坏该数据,我使用stdbuf 运行程序od 行缓冲。此外,由于处理按键的是 bash 脚本,因此原始程序和 awk 脚本都必须在后台运行。因此,需要制定明确的退出策略。当按下q 键时awk 将退出,而当awk 终止时od 将自动终止。

最后是这样的:

#!/usr/bin/env bash

# make a fifo which we use to inject the output of data-stream
# and the key press
mkfifo foo

# start the program in line-buffer mode, writing to FIFO
# and run it in the background
stdbuf -o L  od -vAn -tu2 -w2 < /dev/urandom > foo &

# run the awk program that processes the identified key-press
# also run it in the background and insert a clear EXIT strategy
awk '/key/{if ($2=="q") exit; else p=!p}
     !p{print}
      p{print 0}' foo &

# handle the key pressing
# if a key is pressed inject the string "key <key>" into the FIFO
# use "q" to exit
while true; do
    read -rsn1 key
    echo "key $key" > foo 
    [[ $key == "q" ]] && exit
done

注意:我忽略了密钥必须是z的概念

一些有用的帖子:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-15
    • 2013-01-16
    相关资源
    最近更新 更多