views:

2208

answers:

3

I have a pair of shell programs that talk over a named pipe. The reader creates the pipe when it starts, and removes it when it exits.

Sometimes, the writer will attempt to write to the pipe between the time that the reader stops reading and the time that it removes the pipe.

reader: while condition; do read data <$PIPE; do_stuff; done
writer: echo $data >>$PIPE
reader: rm $PIPE

when this happens, the writer will hang forever trying to open the pipe for writing.

Is there a clean way to give it a timeout, so that it won't stay hung until killed manually? I know I can do

#!/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

but this is kind of icky. Is there a shell builtin or command to do this more cleanly (without breaking out the C compiler)?

+1  A: 

This question comes up periodically (though I couldn't find it with a search). I've written two shell scripts to use as timeout commands: one for things that read standard input and one for things that don't read standard input. This stinks, and I've been meaning to write a C program, but I haven't gotten around to it yet. I'd definitely recommend writing a timeout command in C once and for all. But meanwhile, here's the simpler of the two shell scripts, which hangs if the command reads standard input:

#!/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

The other script is online at http://www.cs.tufts.edu/~nr/drop/timeout.

Norman Ramsey
@Norman, can you post a link to the other script?
profjim
@profjim: posted
Norman Ramsey
thanks for posting!
profjim
+1  A: 

The UNIX "standard" way of dealing with this is to use Expect, which comes with timed-run example: run a program for only a given amount of time.

Expect can do wonders for scripting, well worth learning it. If you don't like Tcl, there is a Python Expect module as well.

Employed Russian
Unfortunately, it appears that this server doesn't have Tcl or Python installed (AIX 5.2). So while that sounds good in general, it doesn't help me. :(
Tim
A nice direct link to the aforementioned Expect example: http://expect.nist.gov/example/timed-run
Aleksander Adamowski
A: 

This pair of programs works much more nicely after being re-written in Perl using Unix domain sockets instead of named pipes. The particular problem in this question went away entirely, since if/when one end dies the connection disappears instead of hanging.

Tim