这回答命令行命令自动杀死一个命令后,一定的时间
建议使用1行方法使bash命令行中运行的命令超时:
( /path/to/slow command with options ) & sleep 5 ; kill $!
但是,给定的“长时间运行”命令可能会在超时之前完成。
(让我们称它为“通常运行时间长但有时很快”的命令,或者简称为tlrbsf。)
所以这个漂亮的一行方法有几个问题。
首先,睡眠不是有条件的,因此在序列完成所花费的时间上设置了一个不希望的下界。当tlrbsf命令在2秒内完成时,考虑30秒或2m甚至5m的睡眠时间——这是非常不可取的。
其次,kill是无条件的,因此该序列将尝试kill一个未运行的进程并抱怨它。
所以…
是否有一种方法可以超时一个典型的长时间运行但有时快速(“tlrbsf”)命令
有一个bash实现(另一个问题已经有Perl和C的答案)
将终止在较早的两个:TLRBSF程序终止,或超时过去
不会杀死不存在/不运行的进程(或者,可选:不会抱怨失败的杀死)
不一定非要是一行字
可以在Cygwin或Linux下运行吗
... 还有,为了加分
在前台运行TLRBSF命令
任何“睡眠”或额外的进程在后台
这样,TLRBSF命令的stdin/stdout/stderr可以重定向,就像它已经直接运行吗?
如果是,请分享你的代码。如果不是,请解释原因。
我花了一段时间试图破解前面提到的例子,但我的bash技能达到了极限。
你完全可以用bash 4.3及以上版本来完成:
_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
Example: _timeout 5 longrunning_command args
Example: { _timeout 5 producer || echo KABOOM $?; } | consumer
Example: producer | { _timeout 5 consumer1; consumer2; }
Example: { while date; do sleep .3; done; } | _timeout 5 cat | less
Needs Bash 4.3 for wait -n
Gives 137 if the command was killed, else the return value of the command.
Works for pipes. (You do not need to go foreground here!)
Works with internal shell commands or functions, too.
Runs in a subshell, so no variable export into the current shell, sorry.
如果你不需要返回代码,这可以变得更简单:
_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }
注:
严格来说你不需要;在;),但是它使事情更加一致;}案例。set +b可能也可以不用,但小心为妙。
除了——forground(可能),你可以实现所有的变量超时支持。——保存状态有点困难,不过。这是留给读者的练习;)
这个配方可以在壳中“自然地”使用(与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 "$@"; ) }
Edit2:错误修复。我注意到退出137是不需要的,同时使_timeout不可靠。
Edit3: git是一个顽固分子,所以它需要双重技巧才能令人满意地工作。
Edit4:在真实GIT示例的第一个_timeout中忘记了_。
我认为这正是你所要求的:
http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3
#!/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 <Dmitry.Golovashkin@sas.com>
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 "$@"