4

I have a script on a Linux machine with a fancy pv piped to a second pv that count a subset of the outputted lines.

Here's the script:

max=1000
for (( i=0; i<max; i++ )); do
  [[ $(shuf -i 1-100 -n 1) -lt 20 ]] && echo REMOVE || echo LEAVE
done | pv -F "%N %b / $(numfmt --to=si $max) %t %p %e" -c -N 'Lookups' -l -s $max \
  | grep --line-buffered '^REMOVE' \
  | pv -F "%N %b / $(numfmt --to=si $max)" -c -N 'Deletes' -l -s $max \
  >/dev/null
stty sane

what I would expect is that the first pv always shows first, and the second always second.

Like this example output:

$ ./fancy_pv.sh
  Lookups: 1.00k / 1.0K 0:00:03 [===============================================================================================================================================================================================================================================================================================================================================================>] 100%            
  Deletes:  189  / 1.0K

But that's not the case, sometimes they swap positions and I see something like this:

$ ./fancy_pv.sh
  Deletes:  199  / 1.0K
  Lookups: 1.00k / 1.0K 0:00:03 [===============================================================================================================================================================================================================================================================================================================================================================>] 100%            

And sometimes I also see something like this:

$ ./fancy_pv.sh
  Lookups:  321  / 1.0K 0:00:01 [===============================================================================================================>                                                                                                                                                                                                                                                 ] 32% ETA 0:00:02
  Deletes:  198  / 1.0K
  Lookups: 1.00k / 1.0K 0:00:03 [===============================================================================================================================================================================================================================================================================================================================================================>] 100%            

I know it must be because of the way pv deletes the line and redraws it, but is there anything I can do to prevent it from messing with the order?

stty sane is there to sanitize the prompt because sometimes pv leaves the terminal unusable.

Thanks

terdon
  • 234,489
  • 66
  • 447
  • 667
Syco
  • 198
  • 5
  • You didn't mention what shell you are using, but since you have `[[` I assumed bash and tagged accordingly. Please change it if that is wrong – terdon Sep 30 '20 at 16:16
  • Thanks for the edit, I use bash. I don't know how `screen` or `tmux` can be used here, hopefully someone can help us figure it out. – Syco Sep 30 '20 at 16:36

1 Answers1

4

The two pv processes in the pipe may start in any order. The output from the latest pv will be in the bottom line.

Delay pv you want in the bottom line. Instead of pv … (where denotes all its arguments) use a subshell:

( </dev/null sleep 1; exec pv … )

In theory the other pv may still start after the delayed one, but in a not-totally-overloaded system it's almost certain the delayed pv will start last.

sleep should not read from its stdin anyway; </dev/null is in case your sleep is weird.

I'm not sure if some race condition can cause an extra (stale) line to appear. If so, delaying one pv should also (almost certainly) help. In my tests the output gets mangled when the terminal needs to be "additionally" updated. Therefore:

  • Do not resize the terminal while pvs are running.
  • Avoid scrolling:
    • Before you run the script, invoke clear (or hit Ctrl+L). This will clear the screen, place the prompt on top and provide room below without the need of scrolling later.
    • Do not type while pvs are running; especially multiple Enters (that could eventually scroll the text) should be avoided.
    • In general do not let anything else than pvs print to the terminal until pvs finish. This applies to other parts of the pipe (e.g. via /dev/tty), asynchronous processes in the script (e.g. simply via their stdout), processes outside of the script (e.g. via /dev/tty* or /dev/pts/*).
Kamil Maciorowski
  • 19,242
  • 1
  • 50
  • 94
  • 1
    Very practical solution. I expected `pv` to do this by itself somehow but neither `pv --wait` nor `pv --delay-start` seem to work reliably for a longer `pv` chain. Well, this output mode is a bit of a hack. – frostschutz Sep 30 '20 at 18:15
  • The delay works really well, when you suggested the subshell I also came out with another idea, this is what I ended up with: `| tee >(/dev/null)` using tee allows me to append more pv to count other lines without altering the output. – Syco Oct 01 '20 at 09:29