Reputation: 26481
GIs there a way to make awk
interactive when it is processing /dev/stdin
via a pipe.
Imagine I have a program which continuously generates data. Example :
$ od -vAn -tu2 -w2 < /dev/urandom
2357
60431
19223
...
This data is being processed by a very advanced awk
script by means of a pipe :
$ od -vAn -tu2 -w2 < /dev/urandom | awk '{print}'
Question: Is it possible to make this awk
program interactive such that :
z
), it starts to output only 0
for each line it reads from the pipe.0
.Problems:
/dev/stdin
(also referenced as -
) is already in use, so the keyboard interaction needs to be picked up with /dev/tty
or is there another way?
getline key < "/dev/tty"
awaits until RS
is encountered, so in the default case you need to press two keys (z and Enter) :
$ awk 'BEGIN{ getline key < "/dev/tty"; print key}'
This is acceptable, but I would prefer a single key-press.
So, is it possible to set RS
locally such that getline
reads a single character? This way we could locally modify RS
and reset it after the getline
. Another way might be using the shell function read
. But it is incompatible between bash
and zsh
.
getline
awaits for input until the end-of-time. So it essentially stops the processing of the pipe. There is a gawk
extention which allows you to set a timeout, but this is only available since gawk 4.2
. So I believe this could potentially work :
awk '{print p ? 0 : $0 }
{ PROCINFO["/dev/tty", "READ_TIMEOUT"]=1;
while (getline key < "/dev/tty") p=key=="z"?!p:p
}
However, I do not have access to gawk 4.2
(update: this does not work)
Requests:
awk
(so I'm thinking multiple pipes here).Upvotes: 1
Views: 1459
Reputation: 26481
After some searching, I came up with a Bash script that allows doing this. The idea is to inject a unique identifiable string into the pipe that awk is processing. Both the original program od
and the bash script write to the pipe. In order not to mangle that data, I used stdbuf
to run the program od
line-buffered. Furthermore, since it is the bash-script that handles the key-press, both the original program and the awk script have to run in the background. Therefore a clean exit strategy needs to be in place. Awk will exit when the key q
is pressed, while od
will terminate automatically when awk is terminated.
In the end, it looks like this :
#!/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
note: I ignored the concept that the key has to be z
Some useful posts :
Upvotes: 1