0

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?

10
  • 1
    mywiki.wooledge.org/DontReadLinesWithFor Commented Nov 2, 2015 at 19:43
  • 1
    ...and you're relying heavily on echo behavior that's not just an extension to POSIX, but actually conflicts with the standard. Consider printf instead. Commented Nov 2, 2015 at 19:44
  • Depending on what you are outputting here you might be able to (ab)use pv for this. Commented Nov 2, 2015 at 19:47
  • I was about to suggest pv as well. dialog also provides a built-in progress bar widget. Commented Nov 2, 2015 at 19:48
  • ...see the question this is closed as a duplicate of for an example of the dialog approach. Commented Nov 2, 2015 at 19:52

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

5 Comments

Much better in terms of following best-practices, but doesn't quite do the updated-in-place progress bar the OP is looking for.
No, but how noisy is "nexec"? The OP can add the bells and whistles
Note that bash can only accomplish integer arithmetic, so it's important to do the multiplication before the division.
As I read it, the question is all about adding bells and whistles.
Yes, the nexec is noisy. Like the original post mentioned, I cleaned up the original code to make it easier for me to test. My script runs multiple commands and then creates some local directories and creates a report. Nothing is printed/echoed to the screen while the script is running.
0
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

2 Comments

This appears to work with some modifications. readarray apparently doesn't work with nsh. One question, however. How does $1 from the write_status function contain the number of items from the text file?
@ramblinman, because "${#servers[@]}" is passed as the first argument to write_status, and is the length of the servers array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.