Limit on number of pipes after long-running command?

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

grep filters lines and shows only lines which contain "press" (case sensitive).

What's the output of xinput test 6 in all cases?

And so?

$ xinput test 6 | grep press | awk '{print $0}'

Here is the output in the terminal when I type unix after executing the command:

$ xinput test 6
key press   30 
key release 30 
key press   57 
key release 57 
key press   31 
key release 31 
key press   53 
key release 53 

This is the output I want (you'd think this would be the output of the third command in my original post when, in fact, that command outputs nothing):

30
57
31
53

--- Post updated at 03:00 PM ---

@nezabudka: The command you suggested generates no output, either.

I don't know what you mean by 6 is the id of my keyboard, which I found by running xinput list , but how about:

xinput test 6 6>&1 | awk '/press/ {print $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:

$ xinput test 6 | awk '/press/ {print $3}'
30
57
31
53

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:

$ xinput test 6 | awk '/press/ {print $3}' | grep 30

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.

Might be due to buffering. Try as a zeroth approximation

xinput test 6 | awk -Winteractive '{print $3}'
1 Like

Both gawk and nawk complain: option `-W interactive` unrecognized, ignored .

But I think we're getting warmer. It does seem to have something to do with buffering.

how about:

xinput test 6 | gawk '{print $3;fflush()}' | grep whatEver

YES! That works. Is there a way to generalize this solution (e.g., at the level of the shell), so that things keep flowing down the pipe?

--- Post updated at 04:09 PM ---

Thank you, RudiC and vgersh99. It was a buffering issue. I found this helpful thread:
shell - Turn off buffering in pipe - Unix & Linux Stack Exchange

I can confirm that either of these pipelines work as expected:

stdbuf -o0 xinput test 9 | stdbuf -o0 grep release | awk '{print $3}'
unbuffer xinput test 9 | unbuffer -p grep release | awk '{print $3}'
1 Like

That's exactly the same thread I found Googling: Turn off buffering in pipe.
It's a combo of stdbuf/unbuffer/cat/script - multiple suggestions.

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:

xinput test 9 | stdbuf -o0 grep release | awk '{print $3}'
xinput test 9 | unbuffer -p grep release | awk '{print $3}'

Guess I need to read up on buffering, since I'm stoked that we found a fix but don't fully understand why we don't have to unbuffer the first command.