Search code examples
linuxterminalescapingansi-escape

How did the Linux operating system display and update progress bars?


For instance, when you pull images from Docker Hub, progress bars (downloading, extracting) in terminal show the progress and are refreshed in accordance with the progress:

9b5a7fa51869: Downloading [==============>                                    ]  8.485MB/29.15MB
9b5a7fa51869: Pull complete
9a72a43d6e84: Extracting [=>                                                 ]  2.785MB/89.96MB

I think if you want to print something to the command line, you put content to some channel or stdout, and it will append to the stream, not update.

I looked up on Internet but I was unable to get any helpful information.

Can you explain how it worked?


Solution

  • If you look at the so-called escape sequences, it is possible to send character sequences which will be interpreted by the terminal as explained here.

    Here is for example, a simple shell script which displays a rule in reverse video and moves the cursor 1 step backward on the rule every seconds:

    #!/bin/sh
    
    [ $# -ne 1 ] && echo "Usage: `basename $0` chrono_value" >&2 && exit 1
    
    # Reverse video (ESC [ 7 m)
    echo -ne "\033[7m"
    
    chrono=$1
    
    # Display of the rule
    i=$1
    while [ $i -gt 0 ]
    do
        echo -n "="
        i=`expr $i - 1`
    done
    
    # Move the cursor backward on the rule
    while [ $chrono -gt 0 ]
    do
        sleep 1
        chrono=`expr $chrono - 1`
        # Move the cursor backward (ESC [ 1 D)
        echo -ne "\033[1D"
        
    done
    
    # Reverse video (ESC [ 7 m)
    echo -ne "\033[7m"
    
    echo
    

    Example of execution:

    enter image description here

    The below script increases the length of an arrow every seconds:

    #!/bin/sh
    
    [ $# -ne 1 ] && echo "Usage: `basename $0` arrow_length" >&2 && exit 1
    
    length=$1
    
    while [ $length -gt 0 ]
    do
      # Move the cursor one step backward to overwrite the preceding arrow's end
      echo -ne "\033[1D=>"
      sleep 1
      length=`expr $length - 1`
    done
    
    echo
    

    Example of execution:

    enter image description here