3

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:

  • For some reason the output is sent to stderr, like with "Permission denied" from find. An easy way to test is to add at the end 2> /dev/null.

    Maybe 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 it 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.

1 Answer 1

4

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.
3
  • Sorry, this was near but doesn't really work for what I want: get rid of the header so I can only have the line starting with "In:", the one that is updated. I don't know why |& grep "^In" doesn't work. Mar 5, 2019 at 4:28
  • @PabloBianchi Oh I’m sorry, I misunderstood your question. Could you edit and clarify, maybe giving an example for the desired output?
    – dessert
    Mar 5, 2019 at 5:49
  • I added an example of the desired output. Using stdbuf -oL tr '\r' '\n' I'm almost there! Are you sure is necessary to process a file on other terminal? It seems it have something to do with the output buffers. Mar 5, 2019 at 7:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .