ramblinman
ramblinman

Reputation: 3

progress indicator for bash script without dialog or pv

I've written a bash script to truncate through a server list and perform various commands. I would like to incorporate a progress indicator like a percent complete while the script is running. I found a script online to perform this but it's not working properly and I am unsure how to utilize it.

The dialog command is not an option here as I am working with nsh

#!/bin/bash

i=0
while [[ $i -lt 11 ]]; do
##
##  \r = carriage return
##  \c = suppress linefeed
##
echo -en "\r$i%\c\b"
(( i=i+1 ))
sleep 1
done

echo

exit 0

For testing purposes I am only connecting to each server and echoing the hostname.

for i in $(cat serverlist.txt)
do
nexec -i hostname
done

How can I utilize the first code snipped to show the progress while going through the list of servers in the code above?

Upvotes: 0

Views: 917

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295480

write_status() {
  done=0
  total=$1
  columns=${COLUMNS:-100}

  # print initial, empty progress bar
  for (( i=0; i<columns; i++ )); do printf '-'; done; printf '\r'

  # every time we read a line, print a progress bar with one more item complete
  while read -r; do
    done=$(( done + 1 ))
    pct=$(( done * columns / total ))
    for ((i=0; i<pct; i++)); do
      printf '+'
    done
    for ((i=pct; i<columns; i++)); do
      printf '-'
    done
    printf '\r'
    if (( done == total )); then break; fi
  done 
}

# read server names into an array; the below is bash 4.x syntax
readarray -t servers <serverlist.txt

# direct FD 4 to the a subshell running the write_status function
exec 4> >(write_status "${#servers[@]}")

for hostname in "${servers[@]}"; do
  nexec -i "$hostname"  # actually run the command
  echo >&4              # ...and notify write_status that it completed
done

If you wanted to parallelize the remote commands, you can do that by replacing the for loop at the bottom with the following:

for hostname in "${servers[@]}"; do
  (nexec -i "$hostname"; echo >&4) &
done
wait

If your shell is a version of bash prior to 4.0, then the readarray -t servers <serverlist can be replaced with the following:

servers=( )
while IFS= read -r server; do servers+=( "$server" ); done <serverlist.txt

Upvotes: 0

glenn jackman
glenn jackman

Reputation: 246847

To keep track of your progress, you'll want to store the list of servers in an array, so you know how many there are:

mapfile -t servers <serverlist.txt    # servers is an array
n=${#servers[@]}                      # how many

i=0
for server in "${servers[@]}"; do
    ((i++))
    progress=$(( i * 100 / n )) 
    nexec ...
    echo "you are ${progress}% complete"
done

Upvotes: 1

Related Questions