Save this as
tkill (make it executable and adjust your
PATH if needed):
trap "exit 143" SIGTERM && kill -- "-$$"
trap _terminate_children SIGINT SIGTERM
eval "$@" | tee >(while :; do
read -t "$tout"
case $? in
0) : ;;
1) break ;;
*) _terminate_children ;;
tkill 30 some_command
The first argument (
30 here) is the timeout in seconds.
some_command to generate text (not binary) output.
stdout of the given command. To include
stderr redirect it like in the last advanced example below.
These are valid examples:
tkill 9 foo -option value
tkill 9 "foo -option value" # equivalent to the above
tkill 5 "foo | bar"
tkill 5 'foo | bar'
tkill 5 'foo | bar | baz' # tkill monitors baz
tkill 5 'foo | bar' | baz # baz reads from tkill
tkill 3 "foo; bar"
tkill 6 "foo && bar || baz"
tkill 7 "some_command 2>&1"
Use Bash syntax in these quotes.
some_command exits by itself then its exit status will be reused as the exit status of
tkill 5 true returns
tkill 5 false returns
tkill 5 "true; false" returns
- If the given timeout expires or
tkill gets interrupted by
SIGTERM then the exit status will be
Fragments of code explained
eval makes the advanced examples possible.
tee allows us to analyze
stdin while still passing a copy of it to
read -t is responsible for applying the timeout, its exit status is used to determine what to do next.
- Command(s) being monitored are killed when needed with this solution.
- Exit status of monitored command(s) is retrieved with this solution.
eval makes the advanced examples possible but you need to remember it does this by evaluating its arguments. Example (somewhat artificial): if you had a file literally named
tkill 9 ls * would expand
* in the current shell,
| would appear as an argument to
eval and it would be interpreted as a pipe operator. In this case
tkill 9 'ls *' is better (but note it expands nothing in the current shell). It's similar with
watch (I mean e.g.
watch ls * vs
watch 'ls *').
- The evaluated command gets piped to
tee, it does not write directly to the terminal. Some commands alter their behavior depending on whether their stdout is a terminal or not. They may colorize and/or columnize their output to a terminal, but not their output to a regular file or a pipe. E.g.
ls --color=auto and
tkill 9 'ls --color=auto' give different output.