Last active
May 4, 2019 01:03
-
-
Save stephencroberts/a96d63d1e297312c4f001d2388a1a10c to your computer and use it in GitHub Desktop.
Shell script to time a command with a timeout (POSIX compliant)
This file contains hidden or 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
#!/bin/sh | |
# | |
# Time a command with a timeout | |
# | |
# Author: Stephen Roberts <[email protected]> | |
# Version: 1 | |
# | |
# Usage: twto [OPTIONS] TIMEOUT COMMAND | |
# | |
# Disable globbing | |
set -f | |
usage() { | |
printf "\nUsage: twto [OPTIONS] TIMEOUT COMMAND\n | |
Time a command with a timeout\n | |
Options: | |
-s, --timeout-success Exit 0 on timeout\n | |
Example: twto 60 ls\n" | |
} | |
err() { | |
printf "\e[31m[ERROR]\t%s\e[0m\n" "$2" >&2 | |
exit "$1" | |
} | |
EXIT_NO_TIME=65 | |
EXIT_TIMED_OUT=67 | |
# Parse arguments | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
-s|--timeout-success) | |
EXIT_TIMED_OUT=0 | |
shift | |
;; | |
*) | |
POSITIONAL="$POSITIONAL $1" | |
shift | |
;; | |
esac | |
done | |
# shellcheck disable=SC2086 | |
set -- $POSITIONAL | |
[ $# -lt 2 ] && usage && exit 0 | |
TIMEOUT="$1" | |
shift | |
COMMAND="$*" | |
command -v /usr/bin/time >/dev/null || err "$EXIT_NO_TIME" \ | |
"Command not found: /usr/bin/time" | |
# Create a named pipe | |
output=$(mktemp -u) | |
mkfifo -m 600 "$output" | |
# Time the command | |
# shellcheck disable=SC2086 | |
( (/usr/bin/time -p $COMMAND>/dev/null) 2>"$output" | |
echo "done" >"$output" ) & | |
time_pid=$! | |
# Start timeout process | |
( trap 'kill -TERM $pid; exit 0' TERM | |
sleep "$TIMEOUT" & | |
pid=$! | |
wait $pid | |
echo timeout >"$output" ) & | |
sleep_pid=$! | |
# Trap exit to clean up | |
trap 'kill -0 "$time_pid" 1>/dev/null 2>&1'\ | |
' && kill "$time_pid"'\ | |
' && wait "$time_pid" 2>/dev/null;'\ | |
'kill -0 "$sleep_pid" 1>/dev/null 2>&1'\ | |
' && kill "$sleep_pid"'\ | |
' && wait "$sleep_pid" 2>/dev/null;'\ | |
'rm -f "$output"' EXIT | |
# Read from pipe until we see either "timeout" or "done" | |
while read -r line; do | |
if [ "$line" = "done" ]; then | |
exit 0 | |
elif [ "$line" = "timeout" ]; then | |
echo "$TIMEOUT" | |
err "$EXIT_TIMED_OUT" "timed out" | |
elif test "${line#*real}" != "$line"; then | |
# shellcheck disable=SC2086 | |
echo ${line#real} | |
exit 0 | |
fi | |
done <"$output" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment