Timeout a command in bash without unnecessary dela

2018-12-31 09:34发布

This answer to Command line command to auto-kill a command after a certain amount of time

proposes a 1-line method to timeout a long-running command from the bash command line:

( /path/to/slow command with options ) & sleep 5 ; kill $!

But it's possible that a given "long-running" command may finish earlier than the timeout. (Let's call it a "typically-long-running-but-sometimes-fast" command, or tlrbsf for fun.)

So this nifty 1-liner approach has a couple of problems. First, the sleep isn't conditional, so that sets an undesirable lower bound on the time taken for the sequence to finish. Consider 30s or 2m or even 5m for the sleep, when the tlrbsf command finishes in 2 seconds — highly undesirable. Second, the kill is unconditional, so this sequence will attempt to kill a non-running process and whine about it.

So...

Is there a way to timeout a typically-long-running-but-sometimes-fast ("tlrbsf") command that

  • has a bash implementation (the other question already has Perl and C answers)
  • will terminate at the earlier of the two: tlrbsf program termination, or timeout elapsed
  • will not kill non-existing/non-running processes (or, optionally: will not complain about a bad kill)
  • doesn't have to be a 1-liner
  • can run under Cygwin or Linux

... and, for bonus points, runs the tlrbsf command in the foreground and any 'sleep' or extra process in the background, such that the stdin/stdout/stderr of the tlrbsf command can be redirected, same as if it had been run directly?

If so, please share your code. If not, please explain why.

I have spent awhile trying to hack the aforementioned example but I'm hitting the limit of my bash skills.

22条回答
只靠听说
2楼-- · 2018-12-31 10:15

A very simplistic way:

# command & sleep 5; pkill -9 -x -f "command"

with pkill (option -f) you can kill your specific command with arguments or specify -n to avoid kill old process.

查看更多
闭嘴吧你
3楼-- · 2018-12-31 10:16

I was presented with a problem to preserve the shell context and allow timeouts, the only problem with it is it will stop script execution on the timeout - but it's fine with the needs I was presented:

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

with the outputs:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

of course I assume there was a dir called scripts

查看更多
若你有天会懂
4楼-- · 2018-12-31 10:18

If you already know the name of the program (let's assume program) to terminate after the timeout (as an example 3 seconds), I can contribute a simple and somewhat dirty alternative solution:

(sleep 3 && killall program) & ./program

This works perfectly if I call benchmark processes with system calls.

查看更多
不流泪的眼
5楼-- · 2018-12-31 10:22

See also the http://www.pixelbeat.org/scripts/timeout script the functionality of which has been integrated into newer coreutils

查看更多
登录 后发表回答