How to resize progress bar according to available space?
I am looking to get an effect where the length of my progress bar resizes accordingly to my PuTTY window. This effect is accomplished with wget's progress bar.
Here is my program I use in my bash scripts to create a progress bar:
_progress_bar
#!/bin/bash
maxwidth=50 # line length (in characters)
filled_char="#"
blank_char="."
current=0 max=0 i=0
current=${1:-0}
max=${2:-100}
if (( $current > $max ))
then
echo >&2 "current value must be smaller max. value"
exit 1
fi
percent=`awk 'BEGIN{printf("%5.2f", '$current' / '$max' * 100)}'`
chars=($current*$maxwidth)/$max
echo -ne " ["
while (( $i < $maxwidth ))
do
if (( $i <= $chars ));then
echo -ne $filled_char
else
echo -ne $blank_char
fi
i=($i+1)
done
echo -ne "] $percent%\r"
if (( $current == $max )); then
echo -ne "\r"
echo
fi
Here is an example of how I use it, this example finds all Tor Onion proxies Exit nodes and bans the IP under a custom chain:
#!/bin/bash
IPTABLES_TARGET="DROP"
IPTABLES_CHAINNAME="TOR"
WORKING_DIR="/tmp/"
# get IP address of eth0 network interface
IP_ADDRESS=$(ifconfig eth0 | awk '/inet addr/ {split ($2,A,":"); print A[2]}')
if ! iptables -L "$IPTABLES_CHAINNAME" -n >/dev/null 2>&1 ; then #If chain doesn't exist
iptables -N "$IPTABLES_CHAINNAME" >/dev/null 2>&开发者_开发技巧amp;1 #Create it
fi
cd $WORKING_DIR
wget -q -O - http://proxy.org/tor_blacklist.txt -U NoSuchBrowser/1.0 > temp_tor_list1
sed -i 's|RewriteCond %{REMOTE_ADDR} \^||g' temp_tor_list1
sed -i 's|\$.*$||g' temp_tor_list1
sed -i 's|\\||g' temp_tor_list1
sed -i 's|Rewrite.*$||g' temp_tor_list1
wget -q -O - "https://check.torproject.org/cgi-bin/TorBulkExitList.py?ip=$IP_ADDRESS&port=80" -U NoSuchBrowser/1.0 > temp_tor_list2
wget -q -O - "https://check.torproject.org/cgi-bin/TorBulkExitList.py?ip=$IP_ADDRESS&port=9998" -U NoSuchBrowser/1.0 >> temp_tor_list2
sed -i 's|^#.*$||g' temp_tor_list2
iptables -F "$IPTABLES_CHAINNAME"
CMD=$(cat temp_tor_list1 temp_tor_list2 | uniq | sort)
UBOUND=$(echo "$CMD" | grep -cve '^\s*$')
for IP in $CMD; do
let COUNT=COUNT+1
_progress_bar $COUNT $UBOUND
iptables -A "$IPTABLES_CHAINNAME" -s $IP -j $IPTABLES_TARGET
done
iptables -A "$IPTABLES_CHAINNAME" -j RETURN
rm temp_tor*
Edit:
I realized that first example people may not want to use so here is a more simple concept:
#!/bin/bash
for i in {1..100}; do
_progress_bar $i 100
done
I made a few changes to your script:
- Converted it to a function. If you want to keep it in a separate file so it's available to multiple scripts, just source the file in each of your scripts. Doing this eliminates the overhead of repeatedly calling an external script.
- Eliminated the while loop (which should have been a
for ((i=0; $i < $maxwidth; i++))
loop anyway) for a drastic speed-up. - Changed your arithmetic expressions so they evaluate immediately instead of setting them to strings for later evaluation.
- Removed dollar signs from variable names where they appear in arithmetic contexts.
- Changed
echo -en
toprintf
. - Made a few other changes
- Changed the AWK output so "100.00%" is decimal aligned with smaller values.
- Changed the AWK command to use variable passing instead of "inside-out: quoting.
Here is the result:
_progress_bar () {
local maxwidth=50 # line length (in characters)
local filled_char="#"
local blank_char="."
local current=0 max=0 i=0
local complete remain
current=${1:-0}
max=${2:-100}
if (( current > max ))
then
echo >&2 "current value must be smaller than max. value"
return 1
fi
percent=$(awk -v "c=$current" -v "m=$max" 'BEGIN{printf("%6.2f", c / m * 100)}')
(( chars = current * maxwidth / max))
# sprintf n zeros into the var named as the arg to -v
printf -v complete '%0*.*d' '' "$chars" ''
printf -v remain '%0*.*d' '' "$((maxwidth - chars))" ''
# replace the zeros with the desired char
complete=${complete//0/"$filled_char"}
remain=${remain//0/"$blank_char"}
printf ' [%s%s] %s%%\r' "$complete" "$remain" "$percent"
}
What was the question? Oh, see BashFAQ/091. Use tput
or bash -i
and $COLUMNS
. If you use bash -i
, however, be aware that it will have the overhead of processing your startup files
After some google searching I did find the following:
tput cols
will return the amount of columns, much like Sdaz's suggested COLUMNS
var.
Therefore I am going with:
maxwidth=$(tput cols)
unless someone else has a more bulletproof way without requiring tput
Bash exports the LINES
and COLUMNS
envvars to the window rows and column counts, respectively. Furthermore, when you resize the putty window, via the SSH or telnet protocols, there is sufficient logic in the protocol to send a WINCH
signal to the active shell, which then resets these values to the new window dimensions.
In your bash script, use the COLUMNS
variable to set the current dimensions, and divide 100 / progbarlen
(progbarlen based on a portion of the COLUMNS variable) to get how many percentage points make up one character, and advance them as your progress moves along. To handle the resizing dynamically, add a handler for SIGWINCH
(via trap
) and have it reread the COLUMNS
envvar, and redraw the progress bar using the new dimensions.
(I haven't tested this in a shell script, and there may be some additional logic required, but this is how bash detects/handles resizing.)
精彩评论