3

Intro

I'm using the pv command in a pipe to show a progress bar. I tried it with a simple counter:

for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | pv --progress --line-mode --size 100 --eta --timer

This works fine, but I'd like the progress bar to show on the same line. This answer explains how to do that.

So I tried this:

for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | >&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer

It stays on one line, but now it doesn't update the ETA anymore.

Question

How can I get the ETA to update too?

Update

Now that iBug answered the question from the previous section, I realized I had one more requirement that's relevant: the stdout needs to be preserved so it can be used in the next pipe. In my specific case I need to write the result to a file (i.e. > some-file.txt)

Rolf W.
  • 1,379
  • 1
  • 15
  • 25

1 Answers1

5

You're typing the wrong command.

for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | >&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer
                                                                               ^

There's a semicolon before pv, so you're actually running it on stdin/stdout, which is your terminal. You should group the extra echo and pv to let it read from the for loop:

for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | (>&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer)

Why this isn't the case for the first command? It is because the whole for do done clause is treated as a single command, so its result correctly gets piped to pv. In the second command, however, the result is piped to echo. You know, that echo doesn't read anything from stdin.

Because pv directs stdin to stdout, the numbers also gets output to the terminal, which mixes up with the indication to stderr. To suppress the nornal output, redirect it to /dev/null, so the final command is

for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | (>&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer) > /dev/null

If you want to redirect the output to a file, just change /dev/null at the end of the command.

iBug
  • 35,554
  • 7
  • 89
  • 134
  • I'm not sure I fully understand what you mean, but I tried this on the command line and now it updates progress on new lines again (like my first example). Is there an essential bit missing? – Rolf W. Dec 14 '17 at 13:53
  • @RolfW. I added a pair of parentheses around `echo ; pv` – iBug Dec 14 '17 at 15:20
  • I noticed, but I now see my comment wasn't clear; What I actually meant is that I don't fully understand the principle behind what you were explaining, so I wasn't able to get it right by myself after your suggestion. When I try your example as-is, it [prints each progression on the next line again](http://prntscr.com/hnrq47), like the first code example in my question. What I'm after is that the progress bar + percentage + ETA updates on the same line. Do you know how to make the code work like that? – Rolf W. Dec 15 '17 at 08:23
  • That's it :) Thank you! – Rolf W. Dec 15 '17 at 11:24
  • For anyone interested, I had one more requirement of writing the stdout to a file at the end of the pipeline: `for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i; done | (>&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer > /dev/null) > temp.txt`. This solution suppresses stdout, so a workaround is needed: `for (( i = 1 ; i <= 100 ; i++ )); do sleep 1; echo $i >> temp.txt; echo "$1"; done | (>&2 echo -en "\r"; pv --progress --line-mode --size 100 --eta --timer > /dev/null)` – Rolf W. Dec 15 '17 at 11:27
  • @RolfW. I wouldn't care if you add this into your question (and I'll update my answer). But generally asking other questions in comments is a bad practice. – iBug Dec 15 '17 at 11:30
  • It was meant as a comment, since I also wrote a solution. But you're right. I added a section `Update` to my question. While writing it, I also realized that a better solution is much more obvious than my workaround :) – Rolf W. Dec 15 '17 at 13:36
  • @RolfW. Your solution is a bit messed up. First you're calling `echo "$i"` twice in each `for` loop, which is redundant, and there's even a typo. Second there's no need to create a copy of output to the file as you can redirect `pv`'s stdout to the target, which is already established in my answer. – iBug Dec 15 '17 at 14:22