我想检测某个进程何时卡住,但它没有给我任何线索。我只知道,如果它在给定的超时时间内(比如说 30 秒)没有产生任何输出,则意味着它可能被阻塞了。
我知道 coreutils 的timeout
程序,但它是基于整个程序退出时间,而不是基于最后一行输出时间。如果这样的方法可行,我会很高兴:
timeout --stdout --stderr 30s my-program
那么有什么办法吗?我该怎么做?
答案1
代码
将其保存为tkill
(使其可执行并PATH
根据需要进行调整):
#!/bin/bash
_terminate_children() {
trap "exit 143" SIGTERM && kill -- "-$$"
}
trap _terminate_children SIGINT SIGTERM
tout="$1"
shift
eval "$@" | tee >(while :; do
read -t "$tout"
case $? in
0) : ;;
1) break ;;
*) _terminate_children ;;
esac
done)
exit "${PIPESTATUS[0]}"
基本用法
tkill 30 some_command
第一个参数(30
这里)是以秒为单位的超时时间。
笔记
tkill
期望some_command
生成文本(非二进制)输出。tkill
stdout
给定命令的探测。要包含stderr
它,请像下面最后一个高级示例一样重定向它。
高级用法
这些都是有效的例子:
tkill 9 foo -option value
tkill 9 "foo -option value" # equivalent to the above
tkill 5 "foo | bar"
tkill 5 'foo | bar'
tkill 5 'foo | bar | baz' # tkill monitors baz
tkill 5 'foo | bar' | baz # baz reads from tkill
tkill 3 "foo; bar"
tkill 6 "foo && bar || baz"
tkill 7 "some_command 2>&1"
在这些引号中使用 Bash 语法。
退出状态
- 如果
some_command
自行退出,则其退出状态将被重用为tkill
;tkill 5 true
返回0
;tkill 5 false
返回1
;tkill 5 "true; false"
返回 的退出状态1
。 - 如果给定的超时时间到期或被
tkill
中断SIGINT
,SIGTERM
则退出状态将为143
。
代码片段解释
eval
使得高级示例成为可能。tee
stdin
允许我们在将其副本传递给的同时进行分析stdout
。read -t
负责应用超时,其退出状态用于确定下一步做什么。- 被监视的命令在需要时被终止此解决方案。
- 监视命令的退出状态可通过以下方式检索此解决方案。
怪癖
eval
使高级示例成为可能,但您需要记住它通过评估其参数来实现这一点。示例(有点人为):如果您有一个名为的文件|
,则将在当前 shell 中tkill 9 ls *
展开,将作为参数出现,并且它将被解释为管道运算符。在这种情况下更好(但请注意,它在当前 shell 中不会展开任何内容)。它类似于*
|
eval
tkill 9 'ls *'
watch
(我的意思是例如watch ls *
vswatch 'ls *'
)。- 评估的命令通过管道传输到
tee
,它不会直接写入终端。某些命令会根据其 stdout 是否为终端来改变其行为。它们可能会将其输出着色和/或列化到终端,但不会将其输出到常规文件或管道。例如ls --color=auto
和tkill 9 'ls --color=auto'
给出不同的输出。
答案2
因此,基本上是这样的:
#!/bin/bash
tmp1=/tmp/tmp-$$-1
tmp2=/tmp/tmp-$$-2
touch $tmp1
touch $tmp2
time_out=30
typeset -i i
i=0
my-program > $tmp1 &
pgmpid=$!
while ps $pgmpid > /dev/null ; do
sleep 1
if diff $tmp1 $tmp2 > /dev/null ; then
i=i+1
if [ $i -gt $time_out ] ; then
kill $pgmpid
fi
else
i=0
cp $tmp1 $tmp2
fi
done
rm -f $tmp1 $tmp2
答案3
在后台运行程序,同时将输出复制到文件。30 秒后,如果文件为空,则终止程序,否则将其返回前台。
my-program | tee temp-file &
sleep 30
[ -s temp-file ] && kill $! || fg $!