I'm trying to create a minimal, crude keylogger for X using only a shell script. I was quickly stumped: Why do these two commands entered in a terminal emulator produce output when I type...
$ xinput test 6 | grep press
$ xinput test 6 | awk '{print $3}'
...but this command produces no output:
$ xinput test 6 | grep press | awk '{print $3}'
?
This observation is really challenging my understanding of stdout, stdin, and pipes.
BTW, 6 is the id of my keyboard, which I found by running xinput list . My shell is bash version 5.0.3
@vgersh99: X input devices have id's, which vary from machine to machine (on my machine, X sees the keyboard as input id=6 and mouse as input id=7, for instance).
Yes, the command you suggested gives the desired output when I type unix:
But it still leaves unanswered why we can only use one pipe after xinput test 6 . For instance, this command produces no output when I type unix, although I'd expect grep to generate some ouput when u is pressed:
What I'm trying to understand is why one pipe after xinput test 6 works fine, but two pipes never produce any output even when output is expected. That's the point of the thread.
well, it's not the "number" of pipes, but the output redirection of xinput :
xinput test 6 6>&1 | awk '/press/ {print $3}'
seems that xinput test 6 put its output not to stdout stream, but to stream with fileDescriptor 6 , but the following awk reads its input from stdin (where stdin has the fd=1).
Hence, we need to map fd=6 of xinput to fd=1: 6>&1 for the awk to read its input from fd=1
No, that's not the issue: xinput test 6 definitely outputs to stdout . Proof is that xinput test 6 6>/dev/null produces output, but xinput test 6 1>/dev/null produces no output.
As I tried to explain, 6 in the command is an argument to xinput that tells it the input device I'm interested in testing. It is not a file descriptor.
Actually, for some reason it is not necessary to turn off buffering in the first command. Only in the second. In other words, these versions also work: