シェルスクリプトのタイムアウトを導入する方法


35

ループが入ったシェルスクリプトを実行したいのですが、それは永遠に続きます。そのため、スクリプト全体にタイムアウトを導入する必要があります。

SuSEでシェルスクリプト全体にタイムアウトを導入するにはどうすればよいですか?


4
これは非常にあいまいな質問です。どのようなタイムアウトについて詳しく説明していただけますか?スクリプト全体のタイムアウト、または単一の関数またはコマンドのタイムアウトが必要ですか?あなたは何をしようとしているのですか?
ティムケネディ

@TimKennedy:今の方が良いことを願っています。
ラデク

1
参照してくださいシェルスクリプトでタイムアウト(私が原因で私の移植性の要件を除外ツールなどのそれの重複を検討していないtimeoutexpect
ジル「SO-停止されて悪」

回答:


30

GNU timeoutが利用できない場合は、使用できますexpect(Mac OS X、BSD、...デフォルトではGNUツールとユーティリティは通常ありません)。

################################################################################
# Executes command with a timeout
# Params:
#   $1 timeout in seconds
#   $2 command
# Returns 1 if timed out 0 otherwise
timeout() {

    time=$1

    # start the command in a subshell to avoid problem with pipes
    # (spawn accepts one command)
    command="/bin/sh -c \"$2\""

    expect -c "set echo \"-noecho\"; set timeout $time; spawn -noecho $command; expect timeout { exit 1 } eof { exit 0 }"    

    if [ $? = 1 ] ; then
        echo "Timeout after ${time} seconds"
    fi

}

編集 例:

timeout 10 "ls ${HOME}"

いいね。画面で実行されたコマンドの出力を表示するにはどうすればよいですか?
ラデク

こんにちは、stdoutはどこにもリダイレクトされないため、出力が表示されるはずです。使用例が追加されました。私の場合、期待通りに私のホームディレクトリの内容を印刷します。どのコマンドが出力を印刷していませんか?
マッテオ

私はそれを適切に呼び出しませんでした。それはうまくいきます!ありがとうございました。タイムアウト後の秒数は出力されません。
ラデック

@Radek申し訳ありませんが修正されました
Matteo

1
GNUタイムアウトを使用timeoutする場合、タイムアウトの場合、それ自体の戻りステータスは124であり、それ以外の場合はコマンドの戻りステータスです(これはTim Kennedyの回答に記載されています)。
クエーサードンキー

15

明確化していただきありがとうございます。

目的を達成する最も簡単な方法はtimeout、GNU Coreutilsパッケージのコマンドのようなラッパー内でループを使用してスクリプトを実行することです。

root@coraid-sp:~# timeout --help            
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
   or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.

Mandatory arguments to long options are mandatory for short options too.
  -k, --kill-after=DURATION
                   also send a KILL signal if COMMAND is still running
                   this long after the initial signal was sent.
  -s, --signal=SIGNAL
                   specify the signal to be sent on timeout.
                   SIGNAL may be a name like 'HUP' or a number.
                   See `kill -l` for a list of signals
      --help     display this help and exit
      --version  output version information and exit

DURATION is an integer with an optional suffix:
`s' for seconds(the default), `m' for minutes, `h' for hours or `d' for days.

If the command times out, then exit with status 124.  Otherwise, exit
with the status of COMMAND.  If no signal is specified, send the TERM
signal upon timeout.  The TERM signal kills any process that does not
block or catch that signal.  For other processes, it may be necessary to
use the KILL (9) signal, since this signal cannot be caught.

Report timeout bugs to bug-coreutils@gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils 'timeout invocation'

最終的には、独自のタイムアウト関数を作成するよりもはるかに簡単になります。タイムアウト関数は、シェルに組み込まれていない傾向があります。


gtimeoutセットアップ(OSX)で呼び出されるため、これがインストールされていることにすぐには気付きませんでした。
ジョシュアゴールドバーグ

7

スクリプト内からウォッチドッグプロセスを起動して、実行時間が長すぎる場合に親を強制終了します。例:

# watchdog process
mainpid=$$
(sleep 5; kill $mainpid) &
watchdogpid=$!

# rest of script
while :
do
   ...stuff...
done
kill $watchdogpid

このスクリプトは、5秒後にウォッチドッグによって終了されます。


6
ただし、毎回タイムアウトを待つ必要があります。この例では、スクリプトがずっと短い場合でも、スクリプトは常に5秒実行されます。ウォッチドッグのPIDも保存して、最後に強制終了する必要があります。
マッテオ

@Matteoフェア。追加されました。
カイルジョーンズ

1
... stuff ...の処理内容によっては、それよりも複雑になる場合があることに注意してください。シェルスクリプトでのタイムアウトを
ジル 'SO-悪であるのを止めてください'

また読む:PIDを一意と見なすことができる期間stackoverflow.com/questions/11323410/linux-pid-recycling
Florian Castellane

3

cratimeoutMartin Cracauerによるものもあります。

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'

2
#!/bin/sh

# Execute a command with a timeout

if [ "$#" -lt "2" ]; then
echo "Usage:   `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi

cleanup()
{
trap - ALRM               #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null &&    #kill last job
  exit 124                #exit with 124 if it was running
}

watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}

watchit $1& a=$!         #start the timeout
shift                    #first param was timeout for sleep
trap "cleanup" ALRM INT  #cleanup after timeout
"$@"& wait $!; RET=$?    #start the job wait for it and save its return value
kill -ALRM $a            #send ALRM signal to watchit
wait $a                  #wait for watchit to finish cleanup
exit $RET                #return the value

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.