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.
How can I get the ETA to update too?
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
)
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.