Formatting grep output with awk. Simple case and background case

awkbufferpipe

I'm struggling with this problem. I have this line:

mplayer *.* 2>/dev/null | grep Playing

which just launchs mplayer and only the line matching Playing filename is printed on screen.
This works also every time I change currently played media.

I was wondering if it was possible to "format" the output of grep. For example, if the output is:

Playing Pink_Floyd-Wish_You_Were_Here.mp3  # I get this at the beginning
Playing Pearl_Jam-Once.mp3                 # I get this after I change media inside mplayer

then I would have something like:

Mplayer: Playing Pink_Floyd-Wish_You_Were_Here.mp3
Mplayer: Playing Pearl_Jam-Once.mp3

To do so, I decided to employ awk:

mplayer *.* 2>/dev/null | grep Playing | awk '{print "Mplayer: "$1}'

But in this case nothing happens; I get no output!!!

My question is not oriented to solve the problem with mplayer, but to have a deeper understanding about pipes in bash.

For example, if I use the following:

echo a_song.mp3 | awk '{print "Mplayer: "$1}'

I get what I want

Mplayer: a_song.mp3

Why does the first command not work?

What do I have to do if I want to run more than one process like this in background?

Best Answer

Many grep implementation will use line buffered when standard output is terminal. When standard output is terminal, it's often an interactive session, you want to get data as soon as possible. So grep will write data to standard output as soon as seeing a newline.

When standard output isn't terminal (often meaning non-interactive session), grep will use block buffer instead. It means grep doesn't write to standard output immediately when seeing a newline. grep collects amount of data (4096 bytes in Linux) before writing to standard output.

Some grep version, like GNU grep or BSD grep have --line-buffered option. It make grep always writing to standard output when seeing a newline. So your example become:

mplayer *.* 2>/dev/null | grep --line-buffered Playing | awk '{print "Mplayer: "$1}'

If your grep version doesn't have --line-buffer, you can do it all with awk:

mplayer *.* 2>/dev/null | awk '/Playing/{print "Mplayer: "$1}'

GNU coreutils has a tool stdbuf for modifying buffering of command. You can always force line buffer in standard output with:

stdbuf -oL command

There's also unbuffer command which disables the output buffering only.

Further reading

Related Question