How to add a progress bar to a shell script?

后端 未结 30 2272
情歌与酒
情歌与酒 2020-11-22 05:48

When scripting in bash or any other shell in *NIX, while running a command that will take more than a few seconds, a progress bar is needed.

For example, copying a b

相关标签:
30条回答
  • 2020-11-22 06:41

    It may be achieved in a pretty simple way:

    • iterate from 0 to 100 with for loop
    • sleep every step for 25ms (0.25 second)
    • append to the $bar variable another = sign to make the progress bar wider
    • echo progress bar and percentage (\r cleans line and returns to the beginning of the line; -ne makes echo doesn't add newline at the end and parses \r special character)
    function progress {
        bar=''
        for (( x=0; x <= 100; x++ )); do
            sleep 0.25
            bar="${bar}="
            echo -ne "$bar ${x}%\r"
        done
        echo -e "\n"
    }
    
    $ progress
    > ========== 10% # here: after 2.5 seconds
    
    $ progress
    > ============================== 30% # here: after 7.5 seconds
    

    COLORED PROGRESS BAR

    function progress {
        bar=''
        for (( x=0; x <= 100; x++ )); do
            sleep 0.05
            bar="${bar} "
    
            echo -ne "\r"
            echo -ne "\e[43m$bar\e[0m"
    
            local left="$(( 100 - $x ))"
            printf " %${left}s"
            echo -n "${x}%"
        done
        echo -e "\n"
    }
    

    To make a progress bar colorful, you can use formatting escape sequence - here the progress bar is yellow: \e[43m, then we reset custom settings with \e[0m, otherwise it would affect further input even when the progress bar is done.

    0 讨论(0)
  • 2020-11-22 06:42

    This is only applicable using gnome zenity. Zenity provides a great native interface to bash scripts: https://help.gnome.org/users/zenity/stable/

    From Zenity Progress Bar Example:

    #!/bin/sh
    (
    echo "10" ; sleep 1
    echo "# Updating mail logs" ; sleep 1
    echo "20" ; sleep 1
    echo "# Resetting cron jobs" ; sleep 1
    echo "50" ; sleep 1
    echo "This line will just be ignored" ; sleep 1
    echo "75" ; sleep 1
    echo "# Rebooting system" ; sleep 1
    echo "100" ; sleep 1
    ) |
    zenity --progress \
      --title="Update System Logs" \
      --text="Scanning mail logs..." \
      --percentage=0
    
    if [ "$?" = -1 ] ; then
            zenity --error \
              --text="Update canceled."
    fi
    
    0 讨论(0)
  • 2020-11-22 06:42

    Using suggestions listed above, I decided to implement my own progress bar.

    #!/usr/bin/env bash
    
    main() {
      for (( i = 0; i <= 100; i=$i + 1)); do
        progress_bar "$i"
        sleep 0.1;
      done
      progress_bar "done"
      exit 0
    }
    
    progress_bar() {
      if [ "$1" == "done" ]; then
        spinner="X"
        percent_done="100"
        progress_message="Done!"
        new_line="\n"
      else
        spinner='/-\|'
        percent_done="${1:-0}"
        progress_message="$percent_done %"
      fi
    
      percent_none="$(( 100 - $percent_done ))"
      [ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
      [ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"
    
      # print the progress bar to the screen
      printf "\r Progress: [%s%s] %s %s${new_line}" \
        "$done_bar" \
        "$none_bar" \
        "${spinner:x++%${#spinner}:1}" \
        "$progress_message"
    }
    
    main "$@"
    
    0 讨论(0)
  • 2020-11-22 06:44

    APT style progress bar (Does not break normal output)

    EDIT: For an updated version check my github page

    I was not satisfied with the responses on this question. What I was personally looking for was a fancy progress bar as is seen by APT.

    I had a look at the C source code for APT and decided to write my own equivalent for bash.

    This progress bar will stay nicely at the bottom of the terminal and will not interfere with any output sent to the terminal.

    Please do note that the bar is currently fixed at 100 characters wide. If you want scale it to the size of the terminal, this is fairly easy to accomplish as well (The updated version on my github page handles this well).

    I will post my script here. Usage example:

    source ./progress_bar.sh
    echo "This is some output"
    setup_scroll_area
    sleep 1
    echo "This is some output 2"
    draw_progress_bar 10
    sleep 1
    echo "This is some output 3"
    draw_progress_bar 50
    sleep 1
    echo "This is some output 4"
    draw_progress_bar 90
    sleep 1
    echo "This is some output 5"
    destroy_scroll_area
    

    The script (I strongly recommend the version on my github instead):

    #!/bin/bash
    
    # This code was inspired by the open source C code of the APT progress bar
    # http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233
    
    #
    # Usage:
    # Source this script
    # setup_scroll_area
    # draw_progress_bar 10
    # draw_progress_bar 90
    # destroy_scroll_area
    #
    
    
    CODE_SAVE_CURSOR="\033[s"
    CODE_RESTORE_CURSOR="\033[u"
    CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
    COLOR_FG="\e[30m"
    COLOR_BG="\e[42m"
    RESTORE_FG="\e[39m"
    RESTORE_BG="\e[49m"
    
    function setup_scroll_area() {
        lines=$(tput lines)
        let lines=$lines-1
        # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
        echo -en "\n"
    
        # Save cursor
        echo -en "$CODE_SAVE_CURSOR"
        # Set scroll region (this will place the cursor in the top left)
        echo -en "\033[0;${lines}r"
    
        # Restore cursor but ensure its inside the scrolling area
        echo -en "$CODE_RESTORE_CURSOR"
        echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
    
        # Start empty progress bar
        draw_progress_bar 0
    }
    
    function destroy_scroll_area() {
        lines=$(tput lines)
        # Save cursor
        echo -en "$CODE_SAVE_CURSOR"
        # Set scroll region (this will place the cursor in the top left)
        echo -en "\033[0;${lines}r"
    
        # Restore cursor but ensure its inside the scrolling area
        echo -en "$CODE_RESTORE_CURSOR"
        echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
    
        # We are done so clear the scroll bar
        clear_progress_bar
    
        # Scroll down a bit to avoid visual glitch when the screen area grows by one row
        echo -en "\n\n"
    }
    
    function draw_progress_bar() {
        percentage=$1
        lines=$(tput lines)
        let lines=$lines
        # Save cursor
        echo -en "$CODE_SAVE_CURSOR"
    
        # Move cursor position to last row
        echo -en "\033[${lines};0f"
    
        # Clear progress bar
        tput el
    
        # Draw progress bar
        print_bar_text $percentage
    
        # Restore cursor position
        echo -en "$CODE_RESTORE_CURSOR"
    }
    
    function clear_progress_bar() {
        lines=$(tput lines)
        let lines=$lines
        # Save cursor
        echo -en "$CODE_SAVE_CURSOR"
    
        # Move cursor position to last row
        echo -en "\033[${lines};0f"
    
        # clear progress bar
        tput el
    
        # Restore cursor position
        echo -en "$CODE_RESTORE_CURSOR"
    }
    
    function print_bar_text() {
        local percentage=$1
    
        # Prepare progress bar
        let remainder=100-$percentage
        progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");
    
        # Print progress bar
        if [ $1 -gt 99 ]
        then
            echo -ne "${progress_bar}"
        else
            echo -ne "${progress_bar}"
        fi
    }
    
    printf_new() {
        str=$1
        num=$2
        v=$(printf "%-${num}s" "$str")
        echo -ne "${v// /$str}"
    }
    
    0 讨论(0)
  • 2020-11-22 06:44
    #!/bin/bash
    
    function progress_bar() {
        bar=""
        total=10
        [[ -z $1 ]] && input=0 || input=${1}
        x="##"
       for i in `seq 1 10`; do
            if [ $i -le $input ] ;then
                bar=$bar$x
            else
                bar="$bar  "
           fi
        done
        #pct=$((200*$input/$total % 2 + 100*$input/$total))
        pct=$(($input*10))
        echo -ne "Progress : [ ${bar} ] (${pct}%) \r"    
        sleep 1
        if [ $input -eq 10 ] ;then
            echo -ne '\n'
        fi
    
    }
    

    could create a function that draws this on a scale say 1-10 for the number of bars :

    progress_bar 1
    echo "doing something ..."
    progress_bar 2
    echo "doing something ..."
    progress_bar 3
    echo "doing something ..."
    progress_bar 8
    echo "doing something ..."
    progress_bar 10
    
    0 讨论(0)
  • 2020-11-22 06:46

    use the linux command pv:

    http://linux.die.net/man/1/pv

    it doesn't know the size if it's in the middle of the stream, but it gives a speed and total and from there you can figure out how long it should take and get feedback so you know it hasn't hung.

    0 讨论(0)
提交回复
热议问题