Differences between revisions 14 and 23 (spanning 9 versions)
Revision 14 as of 2010-04-07 20:02:52
Size: 3299
Editor: GreyCat
Comment:
Revision 23 as of 2019-07-25 13:38:08
Size: 3729
Editor: GreyCat
Comment: so many timeout implementations now
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
There are two C programs that can do this: [[http://pilcrow.madison.wi.us/|doalarm]], and [[http://www.porcupine.org/forensics/tct.html|timeout]].
Compiling them is beyond the scope of this document,
but it should be trivial on GNU/Linux systems, easy on most BSDs,
and at least possible (though potentially painful) on anything else. GNU coreutils also added an implementation of `timeout` in 2008 (version number unknown).
'''FIRST''' check whether the command you're running can be told to timeout directly. The methods described here are "hacky" workarounds to force a command to terminate after a certain time has elapsed. Configuring your command properly is ''always'' preferable to the alternatives below.
Line 8: Line 5:
In early 2010, a
[[http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=commit;h=c403c31e8806b732e1164ef4a206b0eab71bca95|patch]]
was submitted to add a proper
[[http://old.nabble.com/What-signal-should-timeout-send-when-it-gets-a-SIGTERM--ts27814544.html|"--kill-after" option]]
to the "timeout" command in GNU coreutils.
If you have the GNU coreutils version of timeout
(as opposed to the downloaded & compiled "timeout" program mentioned above),
and if your version is new enough to support the "--kill-after" option,
then the coreutils approach is almost certainly your best bet.
If the command has no native support for stopping after a specified time, then you're forced to use an external wrapper. There are a few available now:
Line 18: Line 7:
The primary difference between `doalarm` and `timeout` is that `doalarm` "execs" the program after setting up the alarm, which makes it wonderful in a WrapperScript; while `timeout` launches the program as a child and then hangs around (both processes exist simultaneously), which gives it the opportunity to send more than one signal if necessary.  * Recent GNU coreutils (since at least 2012) has one implementation called [[https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html|timeout]].
 * An older stand-alone `timeout` implementation exists within [[http://www.porcupine.org/forensics/tct.html|TCT]]. This version of `timeout` may be offered as a package in some Linux distributions.
 * Busybox has a third implementation of [[https://busybox.net/downloads/BusyBox.html#timeout|timeout]]. ('''Note''': the coreutils and busybox implementations have incompatible arguments.)
 * A similar program named [[http://pilcrow.madison.wi.us/|doalarm]] also exists.

Beware: by default, some implementations of `timeout` issue a SIGKILL (`kill -9`), which is roughly the same as pulling out the power cord, leaving no chance for the program to commit its work, often resulting in corruption of its data. You should use a signal that allows the program to shut itself down cleanly instead (i.e. SIGTERM). See ProcessManagement for more information on SIGKILL.

Also be aware that some of these wrappers "exec" your program after setting up an alarm, which makes them wonderful to use in [[WrapperScript|wrapper scripts]], while others launch your program as a child and then hang around (because they want to send a second signal if the first one is ignored, or whatever). Be sure to read your tool's documentation.
Line 23: Line 19:
doalarm() { perl -e 'alarm shift; exec @ARGV' "$@"; } doalarm() { perl -e 'alarm shift; exec @ARGV' -- "$@"; }
Line 28: Line 24:
If you can't or won't install one of these programs (which ''really'' should have been included with the basic core Unix utilities 30 years ago!), then the best you can do is an ugly hack like: If you don't even have perl, then the best you can do is an ugly hack like:
Line 31: Line 27:
   command & pid=$!
   { sleep 10; kill $pid; } &
command & pid=$!
{ sleep 10; kill "$pid"; } &
Line 40: Line 36:
   bash -c '(sleep 10; kill $$) & exec command' sh -c '(sleep 10; kill "$$") & exec command'
Line 43: Line 39:
`kill $$` would kill the shell, except that `exec` causes the command to take over the shell's PID. It is necessary to use `bash -c` so that the calling shell isn't replaced; in bash 4, it is possible to use a subshell instead: `kill $$` would kill the shell, except that `exec` causes the command to take over the shell's PID. It is necessary to use `sh -c` so that the calling shell isn't replaced; in bash 4, it is possible to use a subshell instead:
Line 46: Line 42:
   ( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec command ) ( cmdpid=$BASHPID; (sleep 10; kill "$cmdpid") & exec command )
Line 49: Line 45:
The shell-script "[[http://www.shelldorado.com/scripts/cmds/timeout|timeout]]" uses the second approach above. It has the advantage of working immediately (no need for compiling a program), but has problems e.g. with programs reading standard input. The shell-script "[[http://www.shelldorado.com/scripts/cmds/timeout|timeout]]" (not to be confused with any of the commands named `timeout`) uses the second approach above. It has the advantage of working immediately (no need for compiling a program), but has problems e.g. with programs reading standard input.
Line 51: Line 47:
Just use {{{doalarm}}} or {{{timeout}}} instead. Really. But... just use one of the `timeout` or `doalarm` commands instead. Really.

How do I run a command, and have it abort (timeout) after N seconds?

FIRST check whether the command you're running can be told to timeout directly. The methods described here are "hacky" workarounds to force a command to terminate after a certain time has elapsed. Configuring your command properly is always preferable to the alternatives below.

If the command has no native support for stopping after a specified time, then you're forced to use an external wrapper. There are a few available now:

  • Recent GNU coreutils (since at least 2012) has one implementation called timeout.

  • An older stand-alone timeout implementation exists within TCT. This version of timeout may be offered as a package in some Linux distributions.

  • Busybox has a third implementation of timeout. (Note: the coreutils and busybox implementations have incompatible arguments.)

  • A similar program named doalarm also exists.

Beware: by default, some implementations of timeout issue a SIGKILL (kill -9), which is roughly the same as pulling out the power cord, leaving no chance for the program to commit its work, often resulting in corruption of its data. You should use a signal that allows the program to shut itself down cleanly instead (i.e. SIGTERM). See ProcessManagement for more information on SIGKILL.

Also be aware that some of these wrappers "exec" your program after setting up an alarm, which makes them wonderful to use in wrapper scripts, while others launch your program as a child and then hang around (because they want to send a second signal if the first one is ignored, or whatever). Be sure to read your tool's documentation.

If you don't have or don't want one of the above programs, you can use a perl one-liner to set an ALRM and then exec the program you want to run under a time limit. In any case, you must understand what your program does with SIGALRM; programs with periodic updates usually use ALRM for that purpose and update rather than dying when they receive that signal.

doalarm() { perl -e 'alarm shift; exec @ARGV' -- "$@"; }

doalarm ${NUMBER_OF_SECONDS_BEFORE_ALRMING} program arg arg ...

If you don't even have perl, then the best you can do is an ugly hack like:

command & pid=$!
{ sleep 10; kill "$pid"; } &

This will, as you will soon discover, produce quite a mess regardless of whether the timeout condition kicked in or not, if it's run in an interactive shell. Cleaning it up is not something worth my time. Also, it can't be used with any command that requires a foreground terminal, like top.

It is possible to do something similar, but to keep command in the foreground:

sh -c '(sleep 10; kill "$$") & exec command'

kill $$ would kill the shell, except that exec causes the command to take over the shell's PID. It is necessary to use sh -c so that the calling shell isn't replaced; in bash 4, it is possible to use a subshell instead:

( cmdpid=$BASHPID; (sleep 10; kill "$cmdpid") & exec command )

The shell-script "timeout" (not to be confused with any of the commands named timeout) uses the second approach above. It has the advantage of working immediately (no need for compiling a program), but has problems e.g. with programs reading standard input.

But... just use one of the timeout or doalarm commands instead. Really.

BashFAQ/068 (last edited 2019-07-25 13:38:08 by GreyCat)