|
Home > Archive > Unix Programming > September 2005 > alarm() functionality from the shell
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
alarm() functionality from the shell
|
|
| Martin Carpenter 2005-09-16, 7:48 am |
|
Problem: I have a command that runs from the shell, from which I need to
obtain stdout and stderr, and retain the return code ($?). Sometimes, this
command will hang indefinitely, and therefore I'd like to be able to kill it
after a specified time period. (Further detail at the foot of this post).
Given that I need to keep the return code, I don't believe that I can put
the command into the background or use coprocesses (ksh/bash), unless I do
some hackery by writing $? to a temporary file or similar. Ideally, what I'd
like would be a command-line "timeout" utility:
out=`to 180 foo 2>&1`
rc=$?
for a timeout of three minutes. Is there such a thing?
I found some old posts from David Korn, applicable to ksh93, which is almost
what I need, but I only have 88i (Solaris 8):
https://mailman.research.att.com/pi...3q2/000641.html
I also found Michael Wang's "submit":
http://www.unixlabplus.com/unix-prog/submit/submit
which seems a little excessive for my needs (I don't like all of the writing
to temporary files - I'd really like something that I can drop in).
I've written a 200 line C program, a slightly anaemic version of Wang's
"submit", to do what I want, in which I fork(), execvp(), alarm(), waitpid()
and kill() as necessary. This seems fine... but is there a better way?
All suggestions to port JCL/JES will be roundly ignored ;-)
FWIW, what I'm really doing here: running Solaris' smpatch(1M) via ssh.
E.g.,
patches=`ssh -i$idfile root@$machine smpatch analyze 2>&1`
Sometimes, this will hang... indefinitely... I've sought a timeout feature
in ssh, but only found the KeepAlive options.
| |
|
|
Martin Carpenter wrote:
> Problem: I have a command that runs from the shell, from which I need to
> obtain stdout and stderr, and retain the return code ($?). Sometimes, this
> command will hang indefinitely, and therefore I'd like to be able to kill it
> after a specified time period. (Further detail at the foot of this post).
>
> Given that I need to keep the return code, I don't believe that I can put
> the command into the background or use coprocesses (ksh/bash), unless I do
> some hackery by writing $? to a temporary file or similar. Ideally, what I'd
> like would be a command-line "timeout" utility:
>
> out=`to 180 foo 2>&1`
> rc=$?
>
> for a timeout of three minutes. Is there such a thing?
>
> I found some old posts from David Korn, applicable to ksh93, which is almost
> what I need, but I only have 88i (Solaris 8):
>
>
> https://mailman.research.att.com/pi...3q2/000641.html
>
> I also found Michael Wang's "submit":
>
> http://www.unixlabplus.com/unix-prog/submit/submit
>
> which seems a little excessive for my needs (I don't like all of the writing
> to temporary files - I'd really like something that I can drop in).
>
> I've written a 200 line C program, a slightly anaemic version of Wang's
> "submit", to do what I want, in which I fork(), execvp(), alarm(), waitpid()
> and kill() as necessary. This seems fine... but is there a better way?
>
> All suggestions to port JCL/JES will be roundly ignored ;-)
>
>
>
>
>
> FWIW, what I'm really doing here: running Solaris' smpatch(1M) via ssh.
> E.g.,
>
> patches=`ssh -i$idfile root@$machine smpatch analyze 2>&1`
>
> Sometimes, this will hang... indefinitely... I've sought a timeout feature
> in ssh, but only found the KeepAlive options.
Hi
Sorry if I've misunderstood your question, but if you want to
automatically kill the process after 3 minutes you could try something
like:
prog > out 2> err & ( sleep 180; kill $! )
Im guessing $? will no longer work in that scenario though, so you need
to assign it somehow (but im not sure how to do that!)
| |
| Martin Carpenter 2005-09-16, 6:01 pm |
|
"alexs" <spam@alexs.org> wrote:
> Sorry if I've misunderstood your question,
No, I think you got it.
> you could try something like:
>
> prog > out 2> err & ( sleep 180; kill $! )
>
> Im guessing $? will no longer work in that scenario
> though, so you need to assign it somewho (but im not
> sure how to do that!)
Yes, that's exactly my problem :-/ As I said in my intro, I don't think it's
possible to capture $? if you start the process in the background [without
resorting to zany hackery].
| |
|
| > Yes, that's exactly my problem :-/ As I said in my intro,
>I don't think it's possible to capture $? if you start the process
>in the background [without resorting to zany hackery].
I think zany hackery is order of the day:
http://groups.google.com/group/comp...eb442a548c7a0b3
Altho that looks a little excessive. Maybe something like:
( prog > out 2> err; echo $? > rc ) & ( sleep 2; kill $! )
If rc exists, the prog terminated normally and contains the code. else
it was killed.
| |
| Chuck Dillon 2005-09-16, 6:01 pm |
| Martin Carpenter wrote:
>
> I found some old posts from David Korn, applicable to ksh93, which is almost
> what I need, but I only have 88i (Solaris 8):
Use dtksh on Solaris 8 to get ksh93 compatibility...
-- ced
--
Chuck Dillon
Senior Software Engineer
NimbleGen Systems Inc.
| |
| Heiner Steven 2005-09-16, 6:01 pm |
| Martin Carpenter wrote:
> Problem: I have a command that runs from the shell, from which I need to
> obtain stdout and stderr, and retain the return code ($?). Sometimes, this
> command will hang indefinitely, and therefore I'd like to be able to kill it
> after a specified time period. (Further detail at the foot of this post).
http://www.shelldorado.com/scripts/cmds/timeout
Heiner
--
___ _
/ __| |_ _____ _____ _ _ Heiner STEVEN <heiner.steven@nexgo.de>
\__ \ _/ -_) V / -_) ' \ Shell Script Programmers: visit
|___/\__\___|\_/\___|_||_| http://www.shelldorado.com/
| |
| William Ahern 2005-09-16, 6:01 pm |
| Martin Carpenter <mcarpenter@free.fr> wrote:
> Problem: I have a command that runs from the shell, from which I need to
> obtain stdout and stderr, and retain the return code ($?). Sometimes, this
> command will hang indefinitely, and therefore I'd like to be able to kill it
> after a specified time period. (Further detail at the foot of this post).
> Given that I need to keep the return code, I don't believe that I can put
> the command into the background or use coprocesses (ksh/bash), unless I do
> some hackery by writing $? to a temporary file or similar. Ideally, what I'd
> like would be a command-line "timeout" utility:
>
> out=`to 180 foo 2>&1`
> rc=$?
>
> for a timeout of three minutes. Is there such a thing?
>
> I found some old posts from David Korn, applicable to ksh93, which is almost
> what I need, but I only have 88i (Solaris 8):
Personally I don't think using temporary files is hackery. You're simply
doing IPC, something the Unix filesystem was intended to be used for. I use
the following functions in several regression test scripts, and I'm not
afraid to create temporary named pipes or files to store return codes. I'm
using bash, though. I don't know how portable the waitpid implementation is.
In the past I've also written long pipelines where I saved several error
codes and stderr output to intermediate temporary files and never felt
dirty.
#
# Usage: tempnam [<pretty name>]
#
# TMPFILE=$(tempnam "example")
#
function tempnam {
if [ -z "$TMPDIR" ]; then
TMPDIR=/tmp
fi
echo ${TMPDIR}/${1}$(hexdump -n8 -e '"%02x"' /dev/urandom)
return $?
} # tempnam
#
# Usage: waitpid <pid> <timeout>
#
# Return value is the exit value of the process with pid <pid>. A return
# value of 127 means that there was no process <pid>, and a value of 128
# signals that the operation timed out.
#
function waitpid {
waitpid_PID=$1
waitpid_TIMEOUT=$2
waitpid_TIMEDOUT=0
trap 'echo "$PROGNAME: caught SIGALRM waiting for pid $waitpid_PID" >&2; waitpid_TIMEDOUT=1' 14
while [ true ]; do sleep $waitpid_TIMEOUT && kill -14 $$; done &
waitpid_ALARM=$!
wait $waitpid_PID 2>/dev/null
waitpid_STATUS=$?
kill -15 $waitpid_ALARM
wait $waitpid_ALARM 2>/dev/null
if [ $waitpid_TIMEDOUT -eq 1 ]; then
waitpid_STATUS=128
fi
return $waitpid_STATUS
} # waitpid
| |
|
| Martin Carpenter wrote:
> Problem: I have a command that runs from the shell, from which I need to
> obtain stdout and stderr, and retain the return code ($?). Sometimes, this
> command will hang indefinitely, and therefore I'd like to be able to kill it
> after a specified time period. (Further detail at the foot of this post).
Just last Friday I had the same problem and for some reason did
not find this thread and the proposed solution by William Ahern.
I came up with my own solution. I dare to say it is better
commented :-). Tried it with bash, don't know if it works
with just an old sh. Take your pick:
Harald.
#!/bin/sh
########################################
################################
# Runs a command and kills it, if necessary, after a given timeout. If
# the command finishes earlier, this script also finishes earlier.
#
# Parameter:
# $1 - timeout in seconds
# $2 ... - command to run and its parameters and redirections
#
# Exit code:
# 0 - all went well
# 0<c<127 - job exited with this exit code
# >=127 - job was preempted
#
# NOTE: Don't put this into a shell function. It may not work in
# circumstances where the shell using it runs several jobs in the
# background.
#
# (C) Harald Kirsch (Harald at pifpafpuf dontmissthedot de)
########################################
################################
#set -x
# enable job control
set -m
# Fetch the timeout from $1 and execute all other arguments in the
# background.
timeout="$1"; shift
eval "$@" &
# Arrange for a SIGUSR1 to kill the above job. We do not use a pid
($!),
# because it might be wrong. The %1, however, can not be mistaken.
trap 'kill -9 %1 1>/dev/null 2>&1' SIGUSR1
# Arrange for SIGUSR1 to be send to us after the timeout in order to
# trigger the above trap. The $$ uniquely identifies this very
# script, because we make sure not to exit before this timeout job
exits.
(sleep $timeout; kill -SIGUSR1 $$ 1>/dev/null 2>&1) &
# Wait for the subprocess that does the work to exit. It either exits
# because it is finished or because it was killed prematurely. By
# a %1 instead of a pid it even works if the process has already
exited.
wait %1
result=$?
# If the subprocess finished in time, the timeout background process
# may still be running, targeting the pid of this very script. Before
# we exit, we send it home.
kill -9 %2 1>/dev/null 2>&1
# make the exit code available
exit $result
| |
| Martin Carpenter 2005-09-20, 7:49 am |
|
"Chuck Dillon" <spam@nimblegen.com> wrote:
> Use dtksh on Solaris 8 to get ksh93 compatibility...
Thanks. And I don't even need dt for this 
| |
| Martin Carpenter 2005-09-20, 7:49 am |
|
"Heiner Steven" <heiner.steven@nexgo.de> wrote:
> http://www.shelldorado.com/scripts/cmds/timeout
Thank you for that - it's pretty much exactly what I was asking for. I have
a couple of tiny niggles:
1. Despite this line:
exec >/dev/null 0<&1 2>&1
under Linux 2.6.13, bash 2.05b.0(1), I still get:
Terminated
back at the prompt if the command lasts longer than the timeout. (Not a big
deal).
2. I chose a different sequence of signals for my C version:
You have: TERM; HUP; KILL
I chose: TERM; ABRT; KILL
I'd be slightly wary of HUP causing the process to do something odd, such as
respawn itself, after which the KILL might go a little awry.
| |
| Martin Carpenter 2005-09-20, 7:49 am |
|
"William Ahern" <william@wilbur.25thandClement.com> wrote:
> Personally I don't think using temporary files is hackery.
....
> echo ${TMPDIR}/${1}$(hexdump -n8 -e '"%02x"' /dev/urandom)
:-)
Thanks for the reply (as well as the smile!).
> I don't know how portable the waitpid implementation is.
Reasonably so, I think. wait is available in sh, ksh and csh as well as
bash, although the csh flavour doesn't seem to take an argument.
Thanks to all who replied - I think we've succesfully beaten this small
question to death.
|
|
|
|
|