关于bash:在处理管道时与awk交互

Interacting with awk while processing a pipe

当通过管道处理/dev/stdin时,gis有一种方法使awk交互。

假设我有一个持续生成数据的程序。例子:

1
2
3
4
5
$ od -vAn -tu2 -w2 < /dev/urandom
 2357
60431
19223
...

这些数据由非常高级的awk脚本通过管道进行处理:

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

问题:是否有可能使这个awk程序交互,以便:

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

问题:

  • /dev/stdin(也被称为-)已经在使用中,因此需要与/dev/tty进行键盘交互,还是有其他方法?

  • getline key <"/dev/tty"等待,直到遇到RS为止,因此在默认情况下,您需要按两个键(zabbkbd和enter):

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

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

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

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

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

    但是,我没有访问gawk 4.2的权限(更新:这不起作用)

请求:

  • 我更喜欢完全符合POSIX的版本,这是或完全是awk或使用符合POSIX的系统调用
  • 如果不可能,可以使用3.1.7之前的gawk扩展和shell独立系统调用。
  • 最后,我会接受任何shell awk构造,在只有awk连续读取数据的单一条件下(所以我在这里考虑多个管道)。

  • 经过一些搜索,我想出了一个bash脚本,允许这样做。其思想是在awk正在处理的管道中注入一个唯一的可识别字符串。原始程序od和bash脚本都会写入管道。为了不管理这些数据,我使用stdbuf运行程序od行缓冲。此外,因为处理按键的是bash脚本,所以原始程序和awk脚本都必须在后台运行。因此,需要制定一个干净的退出策略。当按下键q时,awk退出,当awk终止时,od自动终止。

    最后,看起来是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #!/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的概念。

    一些有用的帖子:

    • 读取进程在写入进程之前终止时的Unix/Linux管道行为
    • Shell脚本响应按键