2

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.

2

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.
| improve this answer | |
  • 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. – Pablo Bianchi Mar 5 '19 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 '19 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. – Pablo Bianchi Mar 5 '19 at 7:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.