156

I have the following in Bash (In Linux)

for dir in Movies/*
do
  (cd "$dir" && pwd|cut -d \/ -f5|tr -s '\n' ', ' >> ../../movielist &&
  exiftool * -t -s3 -ImageSize -FileType|tr -s '\t' ',' >> ../../movielist )
echo "Movie $movies - $dir ADDED!"
let movies=movies+1
done

But I wish to make it so the "echo" shows the following echo on the next line (Not concatenate with the last echo output but replace it) so to make it look like it is updating. Similar to how a progress bar with percent would show on the same line.

Luis Alvarado
  • 8,837
  • 12
  • 46
  • 60

7 Answers7

264

Well I did not read correctly the man echo page for this.

echo had 2 options that could do this if I added a 3rd escape character.

The 2 options are -n and -e.

-n will not output the trailing newline. So that saves me from going to a new line each time I echo something.

-e will allow me to interpret backslash escape symbols.

Guess what escape symbol I want to use for this: \r. Yes, carriage return would send me back to the start and it will visually look like I am updating on the same line.

So the echo line would look like this:

echo -ne "Movie $movies - $dir ADDED!"\\r

I had to escape the escape symbol so bash would not kill it. That is why you see 2 \ symbols above.

As mentioned by William, printf can also do similar (and even more extensive) tasks like this.

wejoey
  • 216
  • 1
  • 3
  • 14
Luis Alvarado
  • 8,837
  • 12
  • 46
  • 60
  • 17
    Just a note for the future: printf will do exactly the same thing, without any options. The advantage is printf generally behaves similarly in every environment and OS, while echo can sometimes behave very differently. For cross-platform scripts (or if you think you may ever care about that), using printf is best-practice. – William T Froggard Apr 08 '16 at 15:49
  • 12
    `printf a; printf b` outputs `ab` --- `printf a\\r; printf b` outputs `b` --- `printf a\\r; sleep 1; printf b` outputs `a`, then `b` – XavierStuvw Feb 09 '17 at 15:51
  • 1
    The simple fix for the quoting is to put the `\r` inside the double quotes, too. But generally, like others already commented, avoid `echo -e` in favor of `printf`. – tripleee Dec 02 '22 at 06:11
  • @tripleee Thank you. Yes that printf save my butt 10 years ago. – Luis Alvarado Dec 02 '22 at 14:27
78

If I have understood well, you can get it replacing your echo with the following line:

echo -ne "Movie $movies - $dir ADDED! \033[0K\r"

Here is a small example that you can run to understand its behaviour:

#!/bin/bash
for pc in $(seq 1 100); do
    echo -ne "$pc%\033[0K\r"
    sleep 1
done
echo
arutaku
  • 5,937
  • 1
  • 24
  • 38
30

The rest of answers are pretty good, but just wanted to add some extra information in case someone comes here looking for a solution to replace/update a multiline echo.

So I would like to share an example with you all. The following script was tried on a CentOS system and uses "timedatectl" command which basically prints some detailed time information of your system.

I decided to use that command as its output contains multiple lines and works perfectly for the example below:

#!/bin/bash
while true; do
  COMMAND=$(timedatectl) #Save command result in a var.
  echo "$COMMAND" #Print command result, including new lines.

  sleep 3 #Keep above's output on screen during 3 seconds before clearing it

  #Following code clears previously printed lines
  LINES=$(echo "$COMMAND" | wc -l) #Calculate number of lines for the output previously printed
  for (( i=1; i <= $(($LINES)); i++ ));do #For each line printed as a result of "timedatectl"
    tput cuu1 #Move cursor up by one line
    tput el #Clear the line
  done

done

The above will print the result of "timedatectl" forever and will replace the previous echo with updated results.

I have to mention that this code is only an example, but maybe not the best solution for you depending on your needs. A similar command that would do almost the same (at least visually) is "watch -n 3 timedatectl".

But that's a different story. :)

Hope that helps!

Antony Fuentes
  • 1,013
  • 9
  • 13
15

I use printf, it's shorter as it doesn't need flags to do the work:

printf "\rMy static $myvars composed text"
Sergio Abreu
  • 2,686
  • 25
  • 20
  • 1
    I was trying to achieve this from my shell config which was sourcing deeply nested scripts (a calls b calls z....) The script that was invoked finally wasn't able to give me a predictable behavior with `echo -ne`. Thanks for `printf` to come to rescue. Moreover, I believe `printf` has a consistent behavior across platforms. Highly valuable for redistributable work! Thanks for the answer OP! – BhaveshDiwan Jul 15 '21 at 12:31
7

This is very useful, please try it and change as required:

#!/bin/bash
for load in $(seq 1 100); do
    echo -ne "$load % downloded ...\r"
    sleep 1
done
echo "100"
echo "Loaded ..."
J. Scott Elblein
  • 4,013
  • 15
  • 58
  • 94
Mantu
  • 133
  • 1
  • 5
4

You can try this.. My own version of it..

funcc() {
while true ; do 
for i in \| \/ \- \\ \| \/ \- \\; do 
  echo -n -e "\r$1  $i  "
sleep 0.5
done  
#echo -e "\r                                                                                      "
[ -f /tmp/print-stat ] && break 2
done
}

funcc "Checking Kubectl" & &>/dev/null
sleep 5
touch /tmp/print-stat
echo -e "\rPrint Success                  "
Raghu K
  • 41
  • 3
3

My favorite way is called do the sleep to 50. here i variable need to be used inside echo statements.

for i in $(seq 1 50); do
  echo -ne "$i%\033[0K\r"
  sleep 50
done
echo "ended"
Stphane
  • 3,368
  • 5
  • 32
  • 47
  • 2
    this is nice, but the cursor obscures the first character. I guess some spaces in the echo could get around that. – marathon Oct 18 '18 at 00:31
  • I was trying to achieve this from my shell config which was sourcing deeply nested scripts (a calls b calls z....) The script that was invoked finally wasn't able to give me a predictable behavior with `echo -ne`. Thanks for `printf` to come to rescue. Moreover, I believe `printf` has a consistent behavior across platforms. Highly valuable for redistributable work! Thanks for the answer OP. – BhaveshDiwan Jul 15 '21 at 12:30