Ubuntu – CLI line buffer: How to pipe to grep an output stream

bashcommand linepipesox

I have this command (for pomodoros):

play -n synth 25:00 pinknoise

I don't want to silent completely the output (-q option), just the header (grep don't work).

Normal output:

File Size: 94.3T     
 Encoding: n/a           
 Channels: 1 @ 32-bit   
Samplerate: 48000Hz      
Replaygain: off         
 Duration: unknown      

In:0.00% 00:00:01.02 [00:00:00.00] Out:49.2k [======|======] Hd:1.3 Clip:0 

Desire filtered output: 01.02 (this number is updated, like in a cURL or pv progress bar)

How can I grep just that part of the output?

So far:

  • The output is sent stderr, like with "Permission denied" from find. An easy way to test (I thought I tried) is to add at the end 2> /dev/null.

    I think the reason why sox/play output to stderr is because it supports writing the output to standard output (stdout) using the special filename - (see sox man page).

  • But |& grep "^In" won't work. Using |& tee log.txt seems uses the delete character to update the last line.

    I tried grep --line-buffered, unbuffer and stdbuf (after reading this and this) with some great progress:

      play -n synth 25:00 pinknoise  2>&1 | stdbuf -oL tr '\r' '\n' | grep -o '[0-9][0-9]*\.[0-9][0-9] '
    

That's very close!

Is possible to get only just one updated line like was on the original output? Maybe like this loop with echo -ne.

I'm not sure why something like | grep --line-buffered . doesn't work, neither removing trailing newline: | tr -d '\n'. I need something like tail -f -n 1.

Best Answer

  • You can grep the output if you pipe stderr as well, e.g.:

    $ play -n synth 25:00 pinknoise |& grep File
     File Size: 2.52G
    

    From Pipelines section of GNU Bash manual:

    If ‘|&’ is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command.

    However, this will not work for the progress line: CLI commands usually test whether the output is to a terminal and discard updated output if it isn’t. We need a hacky workaround to get around that. First redirect the full output to a file:

    play -n synth 25:00 pinknoise &>sox.log
    

    This blocks the current terminal and you can not just send it to the background because then it discards the progress line again. So to get this line, open a second terminal in the same directory and process the file, e.g.:

    $ grep In sox.log
    In:0.00% 00:00:03.24 [00:00:00.00] Out:156k  [======|======] Hd:0.8 Clip:0
    $ tail -n+10 sox.log; echo
    In:0.00% 00:00:10.24 [00:00:00.00] Out:492k  [!=====|=====!] Hd:1.7 Clip:0
    

    The advantage of using tail is that you also get the Aborted. line when the play exited:

    $ tail -n+10 sox.log;echo
    In:0.00% 00:00:12.80 [00:00:00.00] Out:614k  [======|======] Hd:0.7 Clip:0    
    Aborted.