330

一定時間後にコマンドを自動強制終了するコマンドラインコマンドに対するこの回答

bashコマンドラインから実行時間の長いコマンドをタイムアウトする1行の方法を提案しています:

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

ただし、特定の「長時間実行」コマンドがタイムアウトよりも早く終了する可能性があります。
(「通常は長時間実行されますが、場合によっては高速な」コマンド、または楽しみのためにtlrbsfと呼びましょう。)

したがって、この気の利いたワンライナー アプローチにはいくつかの問題があります。
まず、sleepは条件付きではないため、シーケンスの終了にかかる時間に望ましくない下限が設定されます。tlrbsfコマンドが 2 秒で終了する場合、スリープには 30秒、2 分、さらには 5 分を考慮してください— 非常に望ましくありません。
第 2 に、killは無条件であるため、このシーケンスは実行されていないプロセスを強制終了し、それについて愚痴をこぼそうとします。

そう...

通常は長時間実行されますが、時には高速な ( "tlrbsf" ) コマンドをタイムアウトする方法はありますか?

  • bashの実装があります(他の質問にはすでにPerlとCの回答があります)
  • 2 つのうち早い方で終了します: tlrbsfプログラムの終了、またはタイムアウトの経過
  • 存在しない/実行されていないプロセスを強制終了しません (または、オプションで:不適切な強制終了について文句を言いません)
  • 1ライナーである必要はありません
  • Cygwin または Linux で実行可能

...そして、ボーナスポイント

  • フォアグラウンドでtlrbsfコマンドを実行します
  • バックグラウンドでの「スリープ」または余分なプロセス

直接実行された場合と同じように、 tlrbsfコマンドの stdin/stdout/stderr をリダイレクトできますか?

もしそうなら、あなたのコードを共有してください。そうでない場合は、その理由を説明してください。

前述の例をハックしようとしてしばらく時間を費やしましたが、bash スキルの限界に達しています。

4

24 に答える 24

614

timeoutおそらくcoreutilsでコマンドを探しているでしょう。これは coreutils の一部であるため、技術的には C ソリューションですが、それでも coreutils です。info timeout詳細については。次に例を示します。

timeout 5 /path/to/slow/command with options
于 2011-01-03T03:04:41.683 に答える
40

このソリューションは、bash モニター モードに関係なく機能します。適切なシグナルを使用して your_command を終了できます

#!/bin/sh
( your_command ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

ウォッチャーは、指定されたタイムアウト後に your_command を強制終了します。スクリプトは遅いタスクを待機し、ウォッチャーを終了します。wait別のシェルの子であるプロセスでは機能しないことに注意してください。

例:

  • your_command が 2 秒以上実行され、終了しました

your_command が中断されました

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
  • your_command はタイムアウト (20 秒) の前に終了しました

your_command が終了しました

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
于 2012-06-15T18:29:48.373 に答える
20

これは完全にbash 4.3以上で行うことができます:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
  • 例:_timeout 5 longrunning_command args
  • 例:{ _timeout 5 producer || echo KABOOM $?; } | consumer
  • 例:producer | { _timeout 5 consumer1; consumer2; }
  • 例:{ while date; do sleep .3; done; } | _timeout 5 cat | less

  • には Bash 4.3 が必要ですwait -n

  • コマンドが強制終了された場合は 137 を返し、そうでない場合はコマンドの戻り値を返します。
  • パイプで動作します。(ここでフォアグラウンドに行く必要はありません!)
  • 内部シェル コマンドまたは関数でも動作します。
  • サブシェルで実行されるため、現在のシェルへの変数のエクスポートはありません。申し訳ありません。

戻りコードが必要ない場合は、これをさらに簡単にすることができます。

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }

ノート:

  • 厳密に言えば、;inは必要ありませんが、 -case; )との一貫性が高まります。; }そして、set +bおそらくはそのままにしておくこともできますが、後悔するよりは安全です.

  • (おそらく) を除いて、すべてのバリアントサポート --forgroundを実装できます。はちょっと難しいですけどね。これは、読者の演習として残されています ;)timeout--preserve-status

このレシピは、シェル内で「自然に」使用できます ( の場合と同様に自然ですflock fd)。

(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)

ただし、上記で説明したように、環境変数をこの方法で囲んでいるシェルに再エクスポートすることはできません。

編集:

実際の例: 時間__git_ps1がかかりすぎる場合のタイムアウト (低速の SSHFS リンクなど):

eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }

Edit2: バグ修正。exit 137それが不要_timeoutであると同時に、信頼性が低くなることに気付きました。

Edit3:gitは頑固なので、満足に機能するには二重のトリックが必要です。

Edit4:実世界の GIT の例で_最初に a を忘れました。_timeout

于 2015-03-02T07:00:41.057 に答える
17

私は、少なくとも debian にパッケージがある「timelimit」を好みます。

http://devel.ringlet.net/sysutils/timelimit/

プロセスを強制終了するときに何かを出力し、デフォルトでしばらくすると SIGKILL も送信するため、coreutils の「タイムアウト」よりも少し便利です。

于 2010-08-15T10:05:52.960 に答える
9

timeoutはおそらく最初に試すアプローチです。タイムアウトになった場合は、通知または別のコマンドを実行する必要がある場合があります。かなりの検索と実験を行った後、次のbashスクリプトを思いつきました。

if 
    timeout 20s COMMAND_YOU_WANT_TO_EXECUTE;
    timeout 20s AS_MANY_COMMANDS_AS_YOU_WANT;
then
    echo 'OK'; #if you want a positive response
else
    echo 'Not OK';
    AND_ALTERNATIVE_COMMANDS
fi
于 2015-04-18T13:34:52.837 に答える
9

機能が新しい coreutils に統合されたhttp://www.pixelbeat.org/scripts/timeoutスクリプトも参照してください。

于 2010-02-02T12:42:28.650 に答える
8

ちょっとハックですが、うまくいきます。他のフォアグラウンド プロセスがある場合は機能しません (これを修正するのを手伝ってください!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

実際、「ボーナス」の基準を満たして、それを元に戻すことができると思います。

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa
于 2009-03-26T23:55:33.037 に答える
5

コードが明確な単純なスクリプト。保存先/usr/local/bin/run:

#!/bin/bash

# run
# Run command with timeout $1 seconds.

# Timeout seconds
timeout_seconds="$1"
shift

# PID
pid=$$

# Start timeout
(
  sleep "$timeout_seconds"
  echo "Timed out after $timeout_seconds seconds"
  kill -- -$pid &>/dev/null
) &
timeout_pid=$!

# Run
"$@"

# Stop timeout
kill $timeout_pid &>/dev/null

実行時間が長すぎるコマンドをタイムアウトにします。

$ run 2 sleep 10
Timed out after 2 seconds
Terminated
$

次のコマンドを完了すると、すぐに終了します。

$ run 10 sleep 2
$
于 2017-06-28T20:44:54.193 に答える
4

programタイムアウト後に終了するプログラムの名前 ( と仮定しましょう) が既にわかっている場合(たとえば、3秒)、単純でやや汚れた代替ソリューションを提供できます。

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

システムコールでベンチマークプロセスを呼び出すと、これは完全に機能します。

于 2012-02-01T13:56:10.127 に答える
2

またcratimeout、Martin Cracauer (Unix および Linux システム用に C で記述) によるものもあります。

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 1
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'
于 2013-03-12T11:58:28.940 に答える
2

OS X はまだ bash 4 を使用しておらず、/usr/bin/timeout もありません。そのため、/usr/bin/timeout (Tino の答え)。パラメータの検証、ヘルプ、使用法、および他のシグナルのサポートは、読者の課題です。

# implement /usr/bin/timeout only if it doesn't exist
[ -n "$(type -p timeout 2>&1)" ] || function timeout { (
    set -m +b
    sleep "$1" &
    SPID=${!}
    ("${@:2}"; RETVAL=$?; kill ${SPID}; exit $RETVAL) &
    CPID=${!}
    wait %1
    SLEEPRETVAL=$?
    if [ $SLEEPRETVAL -eq 0 ] && kill ${CPID} >/dev/null 2>&1 ; then
      RETVAL=124
      # When you need to make sure it dies
      #(sleep 1; kill -9 ${CPID} >/dev/null 2>&1)&
      wait %2
    else
      wait %2
      RETVAL=$?
    fi
    return $RETVAL
) }
于 2015-06-30T19:33:12.663 に答える
0

私の問題はおそらく少し異なっていました。リモートマシンで ssh を介してコマンドを開始し、コマンドがハングした場合にシェルと子を強制終了したいと考えています。

私は今、以下を使用しています:

ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'

このようにして、コマンドはタイムアウトが発生した場合は 255 を返し、成功した場合はコマンドのリターンコードを返します。

ssh セッションからのプロセスの強制終了は、対話型シェルとは異なる方法で処理されることに注意してください。ただし、ssh に -t オプションを使用して疑似端末を割り当てることもできるため、対話型シェルのように機能します。

于 2015-07-16T18:48:43.983 に答える
0
#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"
于 2013-01-03T20:56:17.030 に答える
0

シェル コンテキストを保持してタイムアウトを許可するという問題が提示されました。唯一の問題は、タイムアウト時にスクリプトの実行が停止することですが、提示されたニーズには問題ありません。

#!/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

出力で:

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

もちろん、呼ばれるディレクトリがあったと思いますscripts

于 2013-01-11T02:40:54.413 に答える
-1

99% のケースで、答えはタイムアウト ロジックを実装しないことです。タイムアウト ロジックは、ほとんどすべての状況で、何か他の問題があり、代わりに修正する必要があることを示す赤い警告サインです。

プロセスが n 秒後にハングしたり壊れたりすることがありますか? 次に、その理由を見つけて、代わりに修正します。

余談ですが、ストラガーのソリューションを正しく行うには、fg 1 の代わりに wait "$SPID" を使用する必要があります。これは、スクリプトではジョブ制御がないためです (そして、それを有効にしようとするのはばかげています)。さらに、 fg 1 は、スクリプトで以前に他のジョブを開始していないという事実に依存していますが、これは悪い仮定です。

于 2009-03-27T07:34:16.087 に答える
-2

非常に単純な方法:

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

pkill(オプション-f )を使用すると、特定のコマンドを引数で強制終了するか、 -n を指定して古いプロセスを強制終了しないようにすることができます。

于 2015-02-16T10:29:18.847 に答える