你可以完全使用及以上版本来完成这个任务。
_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,否则返回命令的返回值。
- 适用于管道。 (这里不需要进入前台!)
- 也适用于内部 shell 命令或函数。
- 在子 shell 中运行,因此无法导出变量到当前 shell,抱歉。
如果您不需要返回代码,这可以更加简单:
_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }
注意:
这个方法可以在 Shell 中“自然地”使用(就像对于 flock fd
一样):
(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)
然而,正如上面所解释的那样,你不能以自然的方式重新导出环境变量到封闭的shell中。
编辑:
现实世界的例子:如果花费太长时间(例如慢速SSHFS-Links),则超时
__git_ps1
。
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
变得不可靠。
编辑3:git
是一个顽强的家伙,所以需要双重技巧才能令其令人满意地工作。
编辑4:在真实世界的GIT示例中,忘记了第一个_timeout
中的_
。
更新2023-08-06:我找到了一种更好的方法来限制
git
的运行时间,所以上面只是一个例子。
以下不再仅限于bash,因为它需要setsid
。但是,我没有找到只使用bash
习语可靠地创建进程组领导者的方法,抱歉。
这个方法稍微有点复杂,但非常有效,因为它不仅会终止子进程,还会终止子进程在同一进程组中创建的所有内容。
我现在使用以下方法:
__git_ps1() { setsid -w /bin/bash -c 'sleep 1 & . /usr/lib/git-core/git-sh-prompt && __git_ps1 "$@" & wait -n; p=$(/usr/bin/ps --no-headers -opgrp $$) && [ $$ = ${p:-x} ] && /usr/bin/kill -9 0; echo "PGRP mismatch $$ $p" >&2' bash "$@"; }
它的功能是什么:
setsid -w /bin/bash -c 'SCRIPT' bash "$@"
在一个新的进程组中运行 SCRIPT
sleep 1 &
设置超时时间
. /usr/lib/git-core/git-sh-prompt && __git_ps1 "$@" &
并行运行 git 提示符
/usr/lib/git-core/git-sh-prompt
是针对 Ubuntu 22.04 的,如有需要请更改
wait -n;
等待 sleep
或者 __git_ps1
返回
p=$(/usr/bin/ps --no-headers -opgrp $$) && [ $$ = ${p:-x} ] &&
只是一个保护措施,用于检查 setsid
是否成功,并且我们确实是进程组的领导者
kill -9 0
强制杀死整个进程组
- 所有可能仍在执行的
git
命令
- 包括
/bin/bash
echo "PGRP mismatch $$ $p" >&2'
永远不会执行
- 这会通知您,要么
setsid
是伪造的
- 要么其他事情(
kill
?)没有按预期工作
保护措施是为了防止
setsid
不按照广告宣传的方式工作。如果没有这个保护措施,你当前的shell可能会被终止,这将导致无法生成一个交互式shell。
如果你使用这个方法并且信任setsid
,那么你可能不需要这个保护措施,因此setsid
是唯一一个非bash习惯用法所需的。
timeout
实用程序的原因? - Chris Johnsontimeout
真是太棒了!你甚至可以将其用于 多个命令(多行脚本):https://dev59.com/EloT5IYBdhLWcg3w8jJm#61888916 - Noam Manos