Shell – pipe, { list; } only works with some programs

pipeshell

Need explanations from power users for such unpredictable behaviour:

ps -eF | { head -n 1;grep worker; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root       441     2  0     0     0   2 paź15 ?       00:00:00 [kworker/2:1H]

everything looks ok whereas

ls -la / | { head -n 1;grep sbin; }

displays only output from head


I thought about stdout 2>&1 and doesn't work neither
for me it's weird, any explanations or suggest how to handle it?

Best Answer

I did some investigating using strace and it appears to be due to the way the program on the left side of the pipeline does it's writing to the terminal. When the ls command is executed it writes all of the data in a single write(). This causes head to consume all of stdin.

On the other hand ps writes out data in batches, so only the first write() is consumed by head, and then it exists. Later calls to write() will go to the newly spawned grep process.

This means that it would not work if the process you are trying to grep for did not occur in the first write(), since grep does not get to see all of the data (it sees even less than just the data minus the first line).

Here is an example of trying to grep for pid 1 on my system:

$ ps -eF | { head -n2; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | grep '/lib/systemd/systemd$'
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | { head -n1; grep '/lib/systemd/systemd$'; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD

Your ps -eF example only works by chance.