この答え to 一定時間後にコマンドを自動削除するコマンドラインコマンド
bashコマンドラインから長期実行コマンドをタイムアウトするための1行の方法を提案します。
( /path/to/slow command with options ) & sleep 5 ; kill $!
しかし、指定された "長時間実行"コマンドがタイムアウトより早く終了する可能性があります。 (それを "通常は長く走るが時々速い"コマンド、あるいはtlrbsfと呼びましょう。)
そのため、この気の利いた1ライナーアプローチにはいくつかの問題があります。まず、sleep
は条件付きではないので、シーケンスが終了するのにかかる時間に望ましくない下限を設定します。 tlrbsfコマンドが2秒で終了する場合、スリープには30秒、2 m、さらには5 mを考慮してください。非常に望ましくありません。次に、kill
は無条件であるため、このシーケンスは実行されていないプロセスを強制終了して駄目にします。
そう...
通常は実行時間が長くても時々速い("tlrbsf")コマンドをタイムアウトする方法があります。
...そして、ボーナスポイントでは、tlrbsfコマンドをフォアグラウンドで実行し、バックグラウンドで 'sleep'または追加のプロセスを実行します。これにより、tlrbsf]のstdin/stdout/stderrが実行されます。コマンドは、直接実行された場合と同じようにリダイレクトできますか?
もしそうなら、あなたのコードを共有してください。そうでない場合は、その理由を説明してください。
私は前述の例をハックするためにしばらく時間を費やしましたが、私は自分のbashスキルの限界に突き当たります。
私はこれがまさにあなたが求めているものだと思います。
http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout
#!/bin/bash
#
# The Bash Shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.
# Hello Chet,
# please find attached a "little easier" :-) to comprehend
# time-out example. If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <[email protected]>
scriptName="${0##*/}"
declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1
# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY
function printUsage() {
cat <<EOF
Synopsis
$scriptName [-t timeout] [-i interval] [-d delay] command
Execute a command with a time-out.
Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
signal is blocked, then the subsequent SIGKILL (9) terminates it.
-t timeout
Number of seconds to wait for command completion.
Default value: $DEFAULT_TIMEOUT seconds.
-i interval
Interval between checks if the process is still alive.
Positive integer, default value: $DEFAULT_INTERVAL seconds.
-d delay
Delay between posting the SIGTERM signal and destroying the
process by SIGKILL. Default value: $DEFAULT_DELAY seconds.
As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}
# Options.
while getopts ":t:i:d:" option; do
case "$option" in
t) timeout=$OPTARG ;;
i) interval=$OPTARG ;;
d) delay=$OPTARG ;;
*) printUsage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
printUsage
exit 1
fi
# kill -0 pid Exit code indicates if a signal may be sent to $pid process.
(
((t = timeout))
while ((t > 0)); do
sleep $interval
kill -0 $$ || exit 0
((t -= interval))
done
# Be Nice, post SIGTERM first.
# The 'exit 0' below will be executed if any preceeding command fails.
kill -s SIGTERM $$ && kill -0 $$ || exit 0
sleep $delay
kill -s SIGKILL $$
) 2> /dev/null &
exec "$@"
おそらくcoreutilsでtimeout
コマンドを探しているでしょう。これはcoreutilsの一部なので、技術的にはCソリューションですが、それでもcoreutilsです。詳細についてはinfo timeout
。これが例です:
timeout 5 /path/to/slow/command with options
このソリューションは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が中断しました
( 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が終了しました
( 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
そこに行きます:
timeout --signal=SIGINT 10 /path/to/slow command with options
あなたが望むようにSIGINT
と10
を変更することができます;)
少なくともdebianにパッケージがある "timelimit"が好きです。
http://devel.ringlet.net/sysutils/timelimit/
これはcoreutilsの "timeout"よりも少し優れています。なぜならそれはプロセスを終了させるときに何かを表示し、またデフォルトでしばらくしてからSIGKILLを送るからです。
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
wait -n
にはBash 4.3が必要
戻りコードが不要な場合は、さらに簡単にすることができます。
_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }
ノート:
厳密に言うと、;
に; )
は必要ありませんが、; }
の場合により一貫性があります。そしてset +b
もおそらく残しておくことができますが、申し訳ありませんが安全です。
--forground
を除いて(おそらく)あなたはtimeout
がサポートするすべての変種を実装することができます。 --preserve-status
は少し難しいですが。これは読者のための課題として残されています。
このレシピはシェルで「自然に」使うことができます(flock fd
と同じくらい自然)。
(
set +b
sleep 20 &
{
YOUR Shell CODE HERE
} &
wait -n
kill `jobs -p`
)
ただし、上で説明したように、この方法で環境変数を囲んでいるシェルに自然に再エクスポートすることはできません。
編集する
実世界の例:時間がかかりすぎる場合は__git_ps1
をタイムアウトさせる(遅いSSHFS-Linksのようなもののために):
eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }
編集2:バグ修正。私はexit 137
は必要ではなく、同時に_timeout
を信頼できないものにすることに気づきました。
Edit3:git
は大変なので、満足のいく仕事をするにはダブルトリックが必要です。
編集4:実世界のGITの例の最初の_
で_timeout
を忘れてしまった。
http://www.pixelbeat.org/scripts/timeout スクリプトも参照してください。このスクリプトの機能は新しいcoreutilsに統合されています。
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
ちょっとハッキーだけど、うまくいく。他のフォアグラウンドプロセスがある場合は動作しません(これを修正するために私を助けてください!)
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
1秒後にslowcommand
をタイムアウトするには
timeout 1 slowcommand || echo "I failed, perhaps due to time out"
コードを明確にした簡単なスクリプト。 /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
$
タイムアウトの後に終了するプログラムの名前(program
を想定)を既に知っている場合は(例として3
秒)、簡単で多少汚い代替ソリューションを提供できます。
(sleep 3 && killall program) & ./program
私がシステムコールでベンチマークプロセスを呼べば、これは完璧に動作します。
Martin Cracauerによるcratimeout
もあります(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'
OS Xはまだbash 4を使っていませんし、/ usr/bin/timeoutも持っていないので、自作やmacportsを使わずにOS X上で動作する関数は/ usr/bin/timeoutに似ています回答)。パラメータの検証、ヘルプ、使用方法、およびその他のシグナルのサポートは、読者にとっての課題です。
# 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
) }
これは、子プロセスの生成に依存しないバージョンです。この機能を組み込んだスタンドアロンスクリプトが必要でした。それはまた少数のポーリング間隔をする、従ってあなたはより速くポーリングすることができる。タイムアウトが優先されているだろう - しかし私は古いサーバーに立ち往生
# wait_on_command <timeout> <poll interval> command
wait_on_command()
{
local timeout=$1; shift
local interval=$1; shift
$* &
local child=$!
loops=$(bc <<< "($timeout * (1 / $interval)) + 0.5" | sed 's/\..*//g')
((t = loops))
while ((t > 0)); do
sleep $interval
kill -0 $child &>/dev/null || return
((t -= 1))
done
kill $child &>/dev/null || kill -0 $child &>/dev/null || return
sleep $interval
kill -9 $child &>/dev/null
echo Timed out
}
slow_command()
{
sleep 2
echo Completed normally
}
# wait 1 sec in 0.1 sec increments
wait_on_command 1 0.1 slow_command
# or call an external command
wait_on_command 1 0.1 sleep 10
私はphpスクリプトを呼び出すcronジョブを持っています、そして時々、それはphpスクリプトで動けなくなります。この解決策は私にとって完璧でした。
私が使う:
scripttimeout -t 60 /script.php
#! /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 "$@"
私の問題は少し違うかもしれません:私はリモートマシン上でssh経由でコマンドを起動し、コマンドがハングしたらシェルとチャイルドを殺したいのです。
私は今、以下を使います。
ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'
これにより、タイムアウトが発生した場合はコマンドが255を返し、成功した場合はコマンドのreturncodeを返します。
Sshセッションからのプロセスの強制終了は対話型シェルとは異なる方法で処理されることに注意してください。しかし、擬似端末を割り当てるためにsshに-tオプションを使用することもできます。そのため、対話的なシェルのように動作します。
シェルのコンテキストを保持し、タイムアウトを許可するという問題が発生しました。それに関する唯一の問題は、タイムアウトでスクリプトの実行が停止することです - ただし、提示されたニーズには問題ありません。
#!/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
というディレクトリがあったと思います
上に構築 @ loup's answer ...
プロセスをタイムアウトしてkill job/pidの出力を黙らせるには、次のコマンドを実行します。
( (sleep 1 && killall program 2>/dev/null) &) && program --version
これはバックグラウンドプロセスをサブシェルに入れるので、ジョブの出力は表示されません。