Last active
August 12, 2022 05:25
-
-
Save elundmark/59dcb9ada7263ee3ff68 to your computer and use it in GitHub Desktop.
Verbose Sleep - echo the progress to stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
get_ms () { | |
date +'%s%N' | grep -oP '^[0-9]{13}' | |
} | |
show_usage () { | |
# ******************************************************************* | |
read -d '' help_text <<- EOF | |
vsleep - verbose sleep | |
Mimic sleep but also output the status and progress to stderr. | |
This scripts license: | |
Date: 2015-12-07 12:30 Author: Erik Lundmark <e@3r1k.se> | |
License: MIT <http://opensource.org/licenses/MIT> | |
License for sleep: | |
Copyright © 2014 Free Software Foundation, Inc. | |
License GPLv3+: GNU GPL version 3 or later | |
<http://gnu.org/licenses/gpl.html> | |
WARNING: | |
Just like sleep you should not expect an exact time | |
to elapse, and this issue is even more prominent | |
using this script; sleeping for 1 hour with a '-i' interval | |
set to 1 will almost add a minute to the actual time elapsed. | |
Increase the interval '-i' to a larger number than the | |
default 5 to lessen the effects of this. | |
USAGE: | |
/path/to/vsleep OPTION NUMBER[SUFFIX] | |
OPTIONS: | |
-v | |
Show more output (seconds and seconds elapsed). | |
-i NUMBER | |
Interval between reports to stderr. | |
The default interval is 5 seconds - see WARNING | |
If the NUMBER[SUFFIX] argument(s) is smaller | |
than this number it will be counted as a | |
"short" sleep and will only output the status | |
on start and exit. | |
EOF | |
# ******************************************************************* | |
echo "$help_text" 1>&2 | |
} | |
# $ time sleep 1s 2s 3s 4s | |
# > real 0m10.005s | |
# $ time /path/to/vsleep 0.4m 0.4m 0.2m 2>/dev/null | |
# > real 1m0.157s | |
status_line () { | |
local w="$1" | |
local I="$2" | |
local s="$3" | |
local v="$4" | |
local e="" | |
if [[ $w -ge 0 ]] ; then | |
if [[ $w -eq 0 ]] ; then | |
e=" 0% - sleeping" | |
else | |
e="$(echo "scale=3; ( $(( w * I )) / $s ) * 100" | bc | grep -oP '^[0-9]+')" | |
[[ ! $e =~ ^[0-9] ]] && e=0 | |
e="$(printf "%5s" "$e""%") - sleeping" | |
fi | |
[[ $v -eq 1 ]] && e="$e"": $s seconds [$(( w * I ))/$s]" | |
echo -ne "\r""$e" 1>&2 | |
else | |
# final echo (paste over entire line) | |
e=" 100% - finished" | |
[[ $v -eq 1 ]] && e="$e"": $s seconds [$s/$s]" | |
echo -e "\r""$e" 1>&2 | |
fi | |
} | |
declare -i start_ms=0 | |
declare -i delayed_ms=0 | |
declare -i verbose=0 | |
declare -i i=0 | |
declare -i C_EC=1 | |
declare -i E_EC=0 | |
declare -i while_num=0 | |
declare -i interval=5 | |
declare arglen | |
declare args | |
declare sleep_arg | |
declare sec_sleep | |
declare re | |
if [[ $# -eq 0 ]] ; then | |
# let it fail by itself | |
sleep | |
exit $? | |
fi | |
while getopts ":vi:" options ; do | |
case "$options" in | |
v) verbose=1;; | |
i) if [[ $OPTARG =~ ^[0-9]+$ && $OPTARG -gt 0 ]] ; then | |
interval="$OPTARG" | |
else | |
echo "-i takes a positiv integer" 1>&2 | |
exit 1 | |
fi | |
;; | |
h) show_usage | |
exit 0;; | |
\?) show_usage | |
exit 0;; | |
*) show_usage | |
exit 1;; | |
esac | |
done | |
args=( "${@:OPTIND}" ) | |
arglen=${#args[@]} | |
for (( i = 0; i < arglen; i++ )) ; do | |
start_ms=$(get_ms) | |
while_num=0 | |
sleep_arg="${args[i]}" | |
# read the manual for sleep for the valid arguments | |
# remember to allow any floating numbers | |
re='^([0-9.]+)([smhd]?)$' | |
if [[ $sleep_arg =~ $re && $(wc -l <<< "$(grep -oP '\.' <<< "$sleep_arg")") -le 1 ]] ; then | |
# '1.0s' --> BASH_REMATCH=('1.0s' 1.0' 's') | |
# convert the argument to whole seconds | |
case "${BASH_REMATCH[2]}" in | |
's') sec_sleep="${BASH_REMATCH[1]}";; | |
'm') sec_sleep="$(echo "scale=0; ${BASH_REMATCH[1]} * 60" | bc)";; | |
'h') sec_sleep="$(echo "scale=0; ${BASH_REMATCH[1]} * 60 * 60" | bc)";; | |
'd') sec_sleep="$(echo "scale=0; ${BASH_REMATCH[1]} * 60 * 60 * 24" | bc)";; | |
*) sec_sleep="${BASH_REMATCH[1]}";; | |
esac | |
# capture new match and check that it's larger than $interval | |
if [[ $sec_sleep =~ ^([0-9]+) && ${BASH_REMATCH[1]} -gt $interval ]] ; then | |
sec_sleep="${BASH_REMATCH[1]}" | |
# divide by 10 to get the number of (manual sleep 10s) loops | |
# sleep_div should always be 1 or greater | |
sleep_div="$(echo "scale=0; $sec_sleep / $interval" | bc)" | |
status_line "$while_num" "$interval" "$sec_sleep" "$verbose" | |
while [[ $while_num -lt $sleep_div ]] ; do | |
# exit status should be the EC of sleep | |
sleep "$interval""s"; C_EC=$? | |
let while_num++ | |
status_line "$while_num" "$interval" "$sec_sleep" "$verbose" | |
done | |
if [[ $interval -gt 1 ]] ; then | |
# sleep for the % remainder | |
sleep "$(echo "scale=0; $sec_sleep % $interval" | bc)"s; C_EC=$? | |
fi | |
status_line '-1' "$interval" "$sec_sleep" "$verbose" | |
else | |
status_line 0 "$interval" "$sec_sleep" "$verbose" | |
sleep "$sleep_arg"; C_EC=$? | |
status_line '-1' "$interval" "$sec_sleep" "$verbose" | |
fi | |
else | |
# let it fail (or succeed) naturally | |
echo "" 1>&2 | |
echo "$(basename "$0"): not a valid argument" 1>&2 | |
sleep "$sleep_arg"; C_EC=$? | |
fi | |
if [[ $E_EC -eq 0 && $C_EC -ne 0 ]] ; then | |
E_EC="$C_EC" | |
fi | |
delayed_ms=$(echo "scale=0; $delayed_ms + ( ( $(get_ms) - $start_ms ) - ( $sec_sleep * 1000 ) )" | bc | grep -oP '^[0-9]+') | |
done | |
if [[ $E_EC -eq 0 && $C_EC -eq 0 && $verbose -eq 1 ]] ; then | |
echo " ${args[*]} +~ $delayed_ms ms delay" 1>&2 | |
fi | |
exit "$E_EC" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment