Skip to content

Instantly share code, notes, and snippets.

@jlinoff
Last active March 9, 2021 14:44
Show Gist options
  • Save jlinoff/132de624716b97392ce5f670cd00a7b3 to your computer and use it in GitHub Desktop.
Save jlinoff/132de624716b97392ce5f670cd00a7b3 to your computer and use it in GitHub Desktop.
Bash shell script that runs a simple command (like ping) periodically and tests the results.
#!/bin/bash
#
# Run a simple command every 15 seconds and report whether it passes
# or fails. It can be used to check for disconnected hosts (a
# heartbeat test).
#
# The minimum valid interval depends on the time it takes to execute
# the command.
#
# Usage:
# $ ./heartbeat.sh CMD # every 15 seconds, forever
# $ INTERVAL=60 ./heartbeat.sh CMD # every 60 seconds, forever
# $ MAX=15 INTERVAL=60 ./heartbeat.sh CMD # every 60 seconds, for 15 minutes
#
# Example:
# $ ./heartbeat.sh ping -c 10 -i 0.5 my-test-host
# $ ./heartbeat.sh ping -c 10 -i 0.5 my-test-host '>' /dev/null
# $ INTERVAL=60 ./heartbeat.sh ping -c 10 -i 0.5 my-test-host '>' /dev/null
# $ MAX=10 INTERVAL=60 ./heartbeat.sh ping -c 10 -i 0.5 my-test-host '>' /dev/null
#
: ${INTERVAL:=15}
: ${SLEEP:=0.5}
: ${MAX:=0}
CMD="$@"
# For systems like macs that use BSD date.
DATE=gdate
if ! $DATE --version >/dev/null 2>&1 ; then
DATE=date
fi
# Print a bit of status information.
printf 'Cmd : %s\n' "$CMD"
printf 'Interval : %d\n' $INTERVAL
printf 'Sleep : %s\n' $SLEEP
printf 'Max : %d\n' $MAX
if [ -z "$CMD" ] ; then
echo -e "\033[31mERROR: A command must be specified!\033[0m"
exit 1
fi
if ! $DATE --version >/dev/null 2>&1 ; then
echo -e "\033[31mERROR:$LINENO: gnu compatible date program not found \033[0m"
fi
# Iterate forever, keep track of the number of times the test was
# performed in COUNT.
COUNT=0
START=0
while (( 1 )) ; do
SECONDS=$($DATE +'%s')
# Tests occur when the modulus is 0.
if (( (SECONDS % INTERVAL) == 0 )) ; then
(( COUNT++ ))
echo -ne "\033[1m"
printf '%5d %d ' $COUNT $MAX
# Print the time elapsed first.
# The first time through, print 00:00:00.
CURR=$($DATE +'%s')
(( START == 0 )) && START=$CURR
ELAPSED=$(( CURR - START ))
ELAPSED_STR=$($DATE -ud "@$ELAPSED" +'%H:%M:%S' | tr -d '\n')
printf '%s ' $ELAPSED_STR
# Print the current date/time with seconds resolution.
CURR_DATE=$($DATE -Iseconds | tr -d '\n')
# Print and run the command.
echo -n "$CURR_DATE - $CMD"
echo -e "\033[0m"
# Use eval so that users can specify commands like:
# ./tester/.sh ping -c 5 -i 0.5 HOST '>' /dev/null
eval "$CMD"
# Capture the exit status and report the current status.
EXIT_CODE=$?
if (( EXIT_CODE )) ; then
echo -e "\033[31mSTATUS: FAILED $EXIT_CODE $COUNT $ELAPSED_STR $CURR_DATE\033[0m"
else
echo -e "\033[32mSTATUS: PASSED $EXIT_CODE $COUNT $ELAPSED_STR $CURR_DATE\033[0m"
fi
# Exit if the max threshold is exceeded.
# If MAX < 1, then the loop never finishes.
if (( COUNT >= MAX )) ; then
break
fi
# Cross the modulus threshold to avoid running the command
# more than once in the same time interval.
SECONDS=$($DATE +'%s')
while (( (SECONDS % INTERVAL) == 0 )) ; do
sleep 0.1
SECONDS=$($DATE +'%s')
done
fi
# Pause for a bit to avoid continuous date testing.
sleep $SLEEP
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment