[comp.unix.questions] timeout on read; Bourne Shell

goer@ellis.uchicago.edu (Richard L. Goerwitz) (05/26/91)

Is there any elegant way to achive a timeout on a read within
a shell script?  Perhaps even "fairly elegant" would do.  Even
a kludge?
-- 

   -Richard L. Goerwitz              goer%sophist@uchicago.bitnet
   goer@sophist.uchicago.edu         rutgers!oddjob!gide!sophist!goer

heinz@cc.univie.ac.at (05/27/91)

In <1991May26.165314.21533@midway.uchicago.edu> goer@ellis.uchicago.edu (Richard L. Goerwitz) writes:

>Is there any elegant way to achive a timeout on a read within
>a shell script?  Perhaps even "fairly elegant" would do.  Even
>a kludge?
>-- 

>   -Richard L. Goerwitz              goer%sophist@uchicago.bitnet
>   goer@sophist.uchicago.edu         rutgers!oddjob!gide!sophist!goer

Here is a quick 'n' dirty hack. I'm quite sure there are more elegant solutions,
but this is the first that came into my mind.

----- Start of sample script -----
	#! /bin/sh --
	trap ":" 2
	(sleep 1; kill -2 $$) &
	read input
	trap 2
	echo End of script
----- End of sample script -----

Explanation:

1) Use the Bourne Shell as interpreter (you may use any other shell as well).

2) The empty command (:) is associated with signal number 2. You may
use any other signal as well, barring signal 9. You may also associate any other
command with the signal, like
	trap "echo Ouch! That hurt!" 2
In the above script, no action needs to be performed upon receipt of the signal,
so the empty command is used. You can't use empty quotes, since this would mean
to ignore the signal, which would be wrong here.

3) A subshell is started in the background, which performs the following
actions: a) sleep 1 second (insert your timeout here); b) send signal number 2
to the parent shell ($$ is expanded to the PID of the shell executing the
script) after waking up from the sleep.

4) Meanwhile, the 'main' shell executes the read (and, of course, blocks
waiting for input). Upon receipt of the signal from the background process,
it interrupts the read, executes the action associated with the signal in the
trap statement (which is nothing in this example), and continues execution of
the script.

5) In case you have to, reset the action for the signal you used to the default
action.

6) Continue with your script.

Hope this helps. I'd appreciate it if someone posted a more elegant solution
(without sub-processes and stuff like that :)

Greetings,
HH
--
--------------------------------------------------------------------------------
---/     Heinz M. Herbeck                    /    Trust me, I know    /       /-
--/     heinz@sophie.pri.univie.ac.at       /    what I'm doing !    /       /--
-/     Vienna University, Austria          /    (Sledge Hammer)     /       /---
--------------------------------------------------------------------------------

martin@mwtech.UUCP (Martin Weitzel) (05/27/91)

In article <1991May26.165314.21533@midway.uchicago.edu> goer@ellis.uchicago.edu (Richard L. Goerwitz) writes:
>Is there any elegant way to achive a timeout on a read within
>a shell script?  Perhaps even "fairly elegant" would do.  Even
>a kludge?

I just hacked the following short C-Program which may solve your problem.
All you need to know to use it is in the comment at the beginning.

--- cut here ---- cut here ---- cut here ---- cut here ---- cut here --
/*
 * tmdrd -- timed read
 *
 * Can be called in shell scripts to read some variable x as follows:
 *
 * x=`tmdrd`		# times out after 30 seconds, returns empty string
 * x=`tmdrd -12`	# times out after 12 seconds, returns empty string
 * x=`tmdrd -60 y`	# times out after 60 seconds, returns "y"
 *
 * BUGS: This program is a quick hack. There could certainly be many
 * improvements, e.g. checking whether the reason for the bad read was
 * really a timeout, better handling of defaults, flushing of the input
 * waiting before the read and after a time out has hit ...
 *
 * AUTHOR: Martin Weitzel (martin@mwtech.UUCP)
 *
 * Permission to distribute, use, and modify this program is granted to
 * everyone, with one MAJOR EXCEPTION: The program may NOT be used by any
 * company that tries to enforce software patents on their work or copy-
 * rights on their user interfaces. (Sorry, I'm not willing to cooperate
 * with those who are not willing to cooperate with the rest of the world.)
*/

static char USAGE[] = "tmdrd [-seconds] [default]";

#include <stdio.h>
#include <signal.h>

#define TM_DEFAULT 30	/* default time out period */
#define MAX_ANSWER 1000	/* maximum acceptable answer */

static void catcher(sig)
	int sig;
{
	/*empty*/
}
	
main(argc, argv)
	int argc;
	char **argv;
{
	int secs = TM_DEFAULT;
	char buffer[MAX_ANSWER];
	char *answer = "";
	int nread;

	/* check usage */
	if (argc > 3) {
		fprintf(stderr, "Usage %s\n", USAGE);
		exit(1);
	}
	/* extract arguments */
	if (argc > 1 && argv[1][0] == '-') secs = atoi(argv[1] + 1);
	if (argc > 1 && argv[argc-1][0] != '-') answer = argv[argc-1];

	/* wait for input */
	signal(SIGALRM, catcher);
	alarm(secs);
	nread = read(0, buffer, MAX_ANSWER);
	alarm(0);

	/* check for timeout condition */
	if (nread > 0) {
		buffer[nread-1] = '\0'; /* delete newline */
		answer = buffer;
	}

	/* write answer to stdout */
	printf("%s", answer);

	exit(0);
}
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83

daniel@island.COM (Daniel Smith - part of the caffeine generation) (05/30/91)

In <1153@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:

> In article <1991May26.165314.21533@midway.uchicago.edu> goer@ellis.uchicago.edu (Richard L. Goerwitz) writes:
> >Is there any elegant way to achive a timeout on a read within
> >a shell script?  Perhaps even "fairly elegant" would do.  Even
> >a kludge?

	One way is to get the grabchars package, which allows for timeouts
and default answers (and more).

	A line such as:

score=`grabchars -d0 -b -n5 -t10 -r -q 'enter your score >> '`

	Will set $score to 0 if return is pressed as the first character,
or if 10 seconds go by.  -b means output to stdout and stderr (so that you
can see what you are typing, -t is # of seconds to time out, -n is # of
(up to) chars to get, -r means "allow return key".  You can get if from
comp.sources.misc archives on uunet and elsewhere, or directly from me.

				Daniel
-- 
daniel@island.com       Daniel Smith, Island Graphics, (415) 491 0765 x 250(w)
daniel@world.std.com      4000 CivicCenterDrive SanRafael MarinCounty CA 94903
dansmith@well.sf.ca.us      Fax: 491 0402 Disclaimer: Hey, I wrote it, not IG!
falling/yes I'm falling/and she keeps calling/me back again - IJSaF, Beatles