Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Asynchronous version of `sched'
- X-seq: zsh-users 120
- From: pws@xxxxxx (Peter William Stephenson)
- To: zsh-users@xxxxxxxxxxxxxxx (Zsh users list)
- Subject: Asynchronous version of `sched'
- Date: Fri, 3 Nov 1995 14:54:15 +0100 (MET)
While I'm watching endless streams of dubious data go past...
Further to the recent discussion on a version of `sched' which doesn't
just get executed before prompts, here's my first attempt at such a
function. It runs a background process which sleeps until the time is
up, then sends a signal to the shell, which is waiting with a trap.
(I've used SIGUSR1 for this purpose.) If I've got it right, the
background process should be HUP'd if the shell exits first.
The function maintains a queue of waiting events in the file
~/.asched, and will restart the sentinel process if necessary, so in
principle you can issue an arbitrary series of 'asched' commands in
any order, just like sched.
I haven't written any sched-like management options: you can look at
~/.asched for the list, and edit that and run `asched' on its own (or
even kill off $ASCHED_PROC) if necessary. However, asched -d will
delete the current entry, while asched -n will do that and look for
the next, if there is one. Note ~/.asched always has absolute times,
of course.
Does anyone feel strong enough to investigate the tty problems I
reported in the introduction below?
# asched(): Queue up events to be executed within the shell:
# like sched except that it doesn't wait for prompts.
# Note, however, that jobs run when the shell is
# not active can do strange things to the tty settings.
# Must be a function, not a script.
#
# Author: Peter Stephenson <pws@xxxxxx>
#
# Usage:
# asched [+]hh:mm commands ...
# (just like sched, with absolute or relative times)
# or
# asched [-nd]
#
# The commands as passed to asched are executed verbatim, so remember
# any internal quotes, e.g.
# asched 16:30 "print \"\\aIt's time to go.\""
#
# With no arguments, asched tries to find a job to execute from the
# list maintained in ~/.asched.
#
# With the option -n, asched deletes the first job in the list
# (whether or not it has run), and tries to find the next. (This is
# used internally by the queuing system).
#
# With -d, asched simply deletes the first job but does not try to
# schedule another. Both -n and -d fail silently.
#
# The variable ASCHED_PROC contains the pid of the background process
# which is counting down to the appropriate time.
#
# Bugs:
# * If you don't have perl in your path, any rescheduling of a job
# which hasn't run will leave a bogus 'sleep' process.
#
# * If you have job control, the current job will be altered (and will
# have been disowned) after the function.
# make options local to function: require extra glob patterns:
# don't complain if =perl doesn't expand.
setopt localoptions extendedglob nonomatch
# make sure we can have multi-line quotations:
# kill -HUP sentinel process when shell exits.
unsetopt cshjunkiequotes nohup
local tm dt hr mn shr smn rel line lcnt rest newcmd newsecs cmd next proc
integer secs minsnow targsecs mins
local ASCHED=~/.asched # file to store info
if [[ $1 = -[nd] ]]; then
# delete first job in list
next=1
shift
# update queue file
if [[ -f $ASCHED ]]; then
tail +2 $ASCHED > $ASCHED.new
if [[ -s $ASCHED.new ]]; then
mv -f $ASCHED.new $ASCHED
else
rm -f $ASCHED.new $ASCHED
fi
fi
# in case sentinel is still running, finish it off
if [[ -n $ASCHED_PROC ]]; then
kill $ASCHED_PROC
unset ASCHED_PROC
unfunction TRAPUSR1
fi
if [[ $1 = -d ]]; then
return 0
fi
else
# look for time and command arguments
tm=$1 # time
shift # command
cmd="$*";
if [[ -n $tm && ( $tm != +#<>:<> || -z $cmd ) ]]; then
# either no arguments, or time and command is present
print "Usage: asched [+]hh:mm command ..." >&2
return 1
fi
if [[ $tm = +* ]]; then
# time is relative to now.
tm=${tm#+}
rel=1
fi
fi
# Find out the time now.
# Can't rely on "date +fmt" being available
dt=$(date)
hr=${dt%%:*} # hour is first thing before colon
mn=${dt#$hr:} # minute is what comes next
hr=${hr##* } # strip words before hour
mn=${mn%% *} # and after minute
mn=${mn%%:<>} # strip possible seconds
((minsnow = hr*60 + mn))
# print Secs now: $((minsnow * 60))
GetRel() {
# return time in seconds to hh:mm passed
# uses minsnow and sets secs
local rhr rmn
rhr=${1%:<>}
rmn=${1#<>:}
((rmn += rhr * 60))
# print Target secs: $((rmn*60))
if ((rmn < minsnow)); then
# add mins in a day to get next morning
((rmn += 24*60))
fi
((secs = (rmn-minsnow)*60))
}
if [[ -n $tm ]]; then
# Process supplied time
shr=${tm%:<>}
smn=${tm#<>:}
if [[ -z $rel]]; then
# It's absolute: find out howmany seconds to target
GetRel $tm
targsecs=$secs
else
# It's relative: find seconds to target
((mins = shr*60 + smn))
((targsecs = mins*60))
# and find out absolute time of event
((mins += minsnow))
if ((mins > 24*60)); then
((mins -= 24*60))
fi
((shr = mins/60))
((smn = mins - shr*60))
fi
# print Secs till event: $targsecs at time ${shr}:${smn}
fi
if [[ -f $ASCHED ]]; then
while read line rest; do
# Loop through existing events in list.
[[ -z $line ]] && break
GetRel $line
# print "Found an event: target $secs"
# See if it's earlier than the new event, if any
[[ -n $cmd ]] && ((targsecs < secs)) && break
# If it was, remember the first such event to be scheduled next.
[[ -z $newsecs ]] && newsecs="$secs" newcmd="$rest"
# If no new command, we only need to look at the first.
[[ -z $cmd ]] && break
((lcnt++))
done < $ASCHED
if ((lcnt)); then
# Add new event in middle of queue file.
{ head -$lcnt $ASCHED
print -n "${shr}:${smn} "
print -R $cmd
tail +$((lcnt+1)) $ASCHED
} > $ASCHED.new
elif [[ -n $cmd ]]; then
# Add new event at beginning of queue file.
{ print -n "${shr}:${smn} "
print -R $cmd
cat $ASCHED
} > $ASCHED.new
fi
if [[ -n $newcmd ]]; then
# Remember earlier event than the one supplied on command line, if any.
targsecs=$newsecs
cmd=$newcmd
fi
# Replace queue file with new one.
[[ -f $ASCHED.new ]] && mv -f $ASCHED.new $ASCHED
elif [[ -n $cmd ]]; then
# No queue file: creat one.
{ print -n "${shr}:${smn} "
print -R $cmd
} > $ASCHED
fi
if [[ -z $cmd ]]; then
# Nothing to execute
if [[ -n $next ]]; then
# If just looking for command after deleting previous one, no error
return 0
else
print "No event found for scheduling." >&2
return 1
fi
fi
# print "Next event is in $targsecs seconds:\n$cmd"
# Kill the old sentinel process if necessary
[[ -n $ASCHED_PROC ]] && kill $ASCHED_PROC
# Define trap to be executed when sent signal by sentinel
eval "TRAPUSR1() {
$cmd
unfunction TRAPUSR1
unset ASCHED_PROC
asched -n
: If you unfunction me you must kill \$ASCHED_PROC, too!
}"
# Start sentinel process to send signal
if [[ =perl != '=perl' ]]; then
# Do the subjob in perl, which saves processes (sleep is internal).
proc="perl -e \"sleep($targsecs); kill 'USR1', $$;\""
else
# Is there some way of getting rid of the sleep if we kill $ASCHED_PROC?
proc="{sleep $targsecs; kill -USR1 $$}";
fi
if [[ -o monitor ]]; then
# Get around job control: we don't want the sentinel to be in
# the job list. Isn't there a better way than this?
eval "$proc &" >&/dev/null
disown
else
eval "$proc &"
fi
ASCHED_PROC=$!
# Tidy up.
unfunction GetRel
return 0
--
Peter Stephenson <pws@xxxxxx> Tel: +49 33762 77366
WWW: http://www.ifh.de/~pws/ Fax: +49 33762 77330
Deutches Electronen-Synchrotron --- Institut fuer Hochenergiephysik Zeuthen
DESY-IfH, 15735 Zeuthen, Germany.
Messages sorted by:
Reverse Date,
Date,
Thread,
Author