我有一对通过命名管道进行通信的 shell 程序。读取器在启动时创建管道,并在退出时删除它。
有时,写入器会在读取器停止读取的时间和删除管道的时间之间尝试写入管道。
reader: while condition; do read data <$PIPE; do_stuff; done
writer: echo $data >>$PIPE
reader: rm $PIPE
发生这种情况时,写入器将永远挂起,试图打开管道进行写入。
是否有一种clean的方法可以给它一个超时时间,这样它就不会保持挂起状态,直到手动杀死?我知道我能行
#!/bin/sh
# timed_write <timeout> <file> <args>
# like "echo <args> >> <file>" with a timeout
TIMEOUT=$1
shift;
FILENAME=$1
shift;
PID=$$
(X=0; # don't do "sleep $TIMEOUT", the "kill %1" doesn't kill the sleep
while [ "$X" -lt "$TIMEOUT" ];
do sleep 1; X=$(expr $X + 1);
done; kill $PID) &
echo "$@" >>$FILENAME
kill %1
但这有点恶心。是否有内置的 shell 或命令可以更干净地执行此操作(无需破坏 C 编译器)?
这对程序在使用 Unix 域套接字而不是命名管道用 Perl 重写后工作得更好。这个问题中的特殊问题完全消失了,因为如果/当一端死亡时,连接就会消失而不是挂起。
这个问题定期出现(尽管我无法通过搜索找到它)。 我编写了两个 shell 脚本用作超时命令:一个用于读取标准输入,另一个用于不读取标准输入。 这太糟糕了,我一直想写一个 C 程序,但我还没有抽出时间。 我绝对建议一劳永逸地用 C 语言编写一个
timeout
命令。 但与此同时,这是两个 shell 脚本中更简单的一个,如果命令读取标准输入,该脚本就会挂起:
#!/bin/ksh
# our watchdog timeout in seconds
maxseconds="$1"
shift
case $# in
0) echo "Usage: `basename $0` <seconds> <command> [arg ...]" 1>&2 ;;
esac
"$@" &
waitforpid=$!
{
sleep $maxseconds
echo "TIMED OUT: $@" 1>&2
2>/dev/null kill -0 $waitforpid && kill -15 $waitforpid
} &
killerpid=$!
>>/dev/null 2>&1 wait $waitforpid
# this is the exit value we care about, so save it and use it when we
rc=$?
# zap our watchdog if it's still there, since we no longer need it
2>>/dev/null kill -0 $killerpid && kill -15 $killerpid
exit $rc
另一个脚本在线位于 http://www.cs.tufts.edu/~nr/drop/timeout。
处理此问题的 UNIX“标准”方法是使用 Expect,它附带定时运行示例:仅在给定的时间内运行程序。
Expect 可以为脚本创造奇迹,非常值得学习。如果您不喜欢 Tcl,还有一个 Python Expect 模块。
trap 'kill $(ps -L $! -o pid=); exit 30' 30
echo kill -30 $$ 2\>/dev/null | at $1 2>/dev/null
shift; eval $@ &
wait