Skip to content

Instantly share code, notes, and snippets.

@stephencroberts
Last active May 4, 2019 01:03

Revisions

  1. stephencroberts revised this gist May 4, 2019. 1 changed file with 12 additions and 6 deletions.
    18 changes: 12 additions & 6 deletions twto.sh
    Original file line number Diff line number Diff line change
    @@ -58,19 +58,24 @@ mkfifo -m 600 "$output"

    # Time the command
    # shellcheck disable=SC2086
    ( (/usr/bin/time -p $COMMAND>/dev/null) 2>"$output"; echo "done" >"$output" ) &
    ( (/usr/bin/time -p $COMMAND>/dev/null) 2>"$output"
    echo "done" >"$output" ) &
    time_pid=$!

    # Start timeout process
    ( sleep "$TIMEOUT" && echo timeout >"$output" ) &
    ( 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" '\
    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" '\
    'kill -0 "$sleep_pid" 1>/dev/null 2>&1'\
    ' && kill "$sleep_pid"'\
    ' && wait "$sleep_pid" 2>/dev/null;'\
    'rm -f "$output"' EXIT

    @@ -84,5 +89,6 @@ while read -r line; do
    elif test "${line#*real}" != "$line"; then
    # shellcheck disable=SC2086
    echo ${line#real}
    exit 0
    fi
    done <"$output"
  2. stephencroberts revised this gist May 4, 2019. 1 changed file with 8 additions and 21 deletions.
    29 changes: 8 additions & 21 deletions twto.sh
    Original file line number Diff line number Diff line change
    @@ -25,7 +25,6 @@ err() {
    }

    EXIT_NO_TIME=65
    EXIT_NO_FP=66
    EXIT_TIMED_OUT=67

    # Parse arguments
    @@ -44,20 +43,22 @@ done
    # shellcheck disable=SC2086
    set -- $POSITIONAL

    [ $# -lt 2 ] && usage && exit 0

    TIMEOUT="$1"
    shift
    COMMAND="$*"
    [ -z "$TIMEOUT" ] && usage && exit 0
    [ -z "$COMMAND" ] && usage && exit 0

    command -v time >/dev/null || err "$EXIT_NO_TIME" "Command not found: time"
    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
    ( ( time $COMMAND>/dev/null ) 2>"$output"; echo "done" >"$output" ) &
    # shellcheck disable=SC2086
    ( (/usr/bin/time -p $COMMAND>/dev/null) 2>"$output"; echo "done" >"$output" ) &
    time_pid=$!

    # Start timeout process
    @@ -81,21 +82,7 @@ while read -r line; do
    echo "$TIMEOUT"
    err "$EXIT_TIMED_OUT" "timed out"
    elif test "${line#*real}" != "$line"; then
    # Parse time command output
    mins=${line%m*}
    mins=${mins#real}
    secs=${line#*m}
    secs=${secs%s}

    # Attempt floating point arithmetic
    if command -v bc >/dev/null; then
    total=$(echo "$mins * 60 + $secs" | bc | xargs -I {} printf '%.3f' {})
    elif command -v perl >/dev/null; then
    total=$(perl -E "say $mins * 60 + $secs")
    else
    err "$EXIT_NO_FP" \
    "No command to handle floating point arithmetic found! Tried bd, perl."
    fi
    echo "$total"
    # shellcheck disable=SC2086
    echo ${line#real}
    fi
    done <"$output"
  3. stephencroberts revised this gist May 3, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion twto.sh
    Original file line number Diff line number Diff line change
    @@ -89,7 +89,7 @@ while read -r line; do

    # Attempt floating point arithmetic
    if command -v bc >/dev/null; then
    total=$(echo "$mins * 60 + $secs" | bc)
    total=$(echo "$mins * 60 + $secs" | bc | xargs -I {} printf '%.3f' {})
    elif command -v perl >/dev/null; then
    total=$(perl -E "say $mins * 60 + $secs")
    else
  4. stephencroberts created this gist May 3, 2019.
    101 changes: 101 additions & 0 deletions twto.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    #!/bin/sh
    #
    # Time a command with a timeout
    #
    # Author: Stephen Roberts <stephenroberts@gmail.com>
    # 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_NO_FP=66
    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

    TIMEOUT="$1"
    shift
    COMMAND="$*"
    [ -z "$TIMEOUT" ] && usage && exit 0
    [ -z "$COMMAND" ] && usage && exit 0

    command -v time >/dev/null || err "$EXIT_NO_TIME" "Command not found: time"

    # Create a named pipe
    output=$(mktemp -u)
    mkfifo -m 600 "$output"

    # Time the command
    ( ( time $COMMAND>/dev/null ) 2>"$output"; echo "done" >"$output" ) &
    time_pid=$!

    # Start timeout process
    ( sleep "$TIMEOUT" && 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
    # Parse time command output
    mins=${line%m*}
    mins=${mins#real}
    secs=${line#*m}
    secs=${secs%s}

    # Attempt floating point arithmetic
    if command -v bc >/dev/null; then
    total=$(echo "$mins * 60 + $secs" | bc)
    elif command -v perl >/dev/null; then
    total=$(perl -E "say $mins * 60 + $secs")
    else
    err "$EXIT_NO_FP" \
    "No command to handle floating point arithmetic found! Tried bd, perl."
    fi
    echo "$total"
    fi
    done <"$output"