I can ask the user to press Enter by using read, and have him wait by calling sleep. But I can’t think of a way of doing both at the same time. I would like the user to be given the choice:
Press Ctrl+C to Cancel, Enter to continue or just wait 10 seconds
How can I do that?
In bash(1), read has a -t option where you can specify a timeout. From the manpage:
read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]
-t timeout: cause read to time out and return failure if a complete line of input is not read within timeout seconds. This option has no effect if read is not reading input from the terminal or a pipe.
Transcript below (without hitting ENTER):
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; echo ; date
Tue Feb 28 22:29:15 WAST 2012
Hit ENTER or wait ten seconds
Tue Feb 28 22:29:25 WAST 2012
Another, hitting ENTER after a couple of seconds:
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; date
Tue Feb 28 22:30:17 WAST 2012
Hit ENTER or wait ten seconds
Tue Feb 28 22:30:19 WAST 2012
And another, hitting CTRL-C:
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; echo ; date
Tue Feb 28 22:30:29 WAST 2012
Hit ENTER or wait ten seconds
(1) If you're doing this in a script, make sure that it's a bash one. You can do that by adding a shebang line at the start such as one of the following:
#!/usr/bin/env bash
#!/bin/bash
The read builtin has a timeout.
read -t 10
will do it
Building on the thoughtful answers above, the return value from the read is useful to distinguish between an empty response from the user (for example "Press Enter" for the default action) and a timeout.
read -t 5 -p "Prompt " RESP
if [[ $? -gt 128 ]] ; then
echo -e "\nTimeout"
else
echo "Response = \"$RESP\"" # adding quotes so empty strings are obvious
fi
Another useful tidbit is that the -p "prompt " is written to stderr (not stdout) so if you're redirecting stderr, the prompt will not be displayed. An example of this would be logging an execution trace to a log file for later analysis. To use read -p Prompt in this case you can redirect stderr to the user for just the read statement.
set -x
exec 2>logfile
read -t 5 -p "Prompt " RESP 2>/dev/tty
From the bash reference manual :
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt][-t timeout][-u fd] [name ...]
Related
I can ask the user to press Enter by using read, and have him wait by calling sleep. But I can’t think of a way of doing both at the same time. I would like the user to be given the choice:
Press Ctrl+C to Cancel, Enter to continue or just wait 10 seconds
How can I do that?
In bash(1), read has a -t option where you can specify a timeout. From the manpage:
read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]
-t timeout: cause read to time out and return failure if a complete line of input is not read within timeout seconds. This option has no effect if read is not reading input from the terminal or a pipe.
Transcript below (without hitting ENTER):
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; echo ; date
Tue Feb 28 22:29:15 WAST 2012
Hit ENTER or wait ten seconds
Tue Feb 28 22:29:25 WAST 2012
Another, hitting ENTER after a couple of seconds:
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; date
Tue Feb 28 22:30:17 WAST 2012
Hit ENTER or wait ten seconds
Tue Feb 28 22:30:19 WAST 2012
And another, hitting CTRL-C:
$ date ; read -t 10 -p "Hit ENTER or wait ten seconds" ; echo ; date
Tue Feb 28 22:30:29 WAST 2012
Hit ENTER or wait ten seconds
(1) If you're doing this in a script, make sure that it's a bash one. You can do that by adding a shebang line at the start such as one of the following:
#!/usr/bin/env bash
#!/bin/bash
The read builtin has a timeout.
read -t 10
will do it
Building on the thoughtful answers above, the return value from the read is useful to distinguish between an empty response from the user (for example "Press Enter" for the default action) and a timeout.
read -t 5 -p "Prompt " RESP
if [[ $? -gt 128 ]] ; then
echo -e "\nTimeout"
else
echo "Response = \"$RESP\"" # adding quotes so empty strings are obvious
fi
Another useful tidbit is that the -p "prompt " is written to stderr (not stdout) so if you're redirecting stderr, the prompt will not be displayed. An example of this would be logging an execution trace to a log file for later analysis. To use read -p Prompt in this case you can redirect stderr to the user for just the read statement.
set -x
exec 2>logfile
read -t 5 -p "Prompt " RESP 2>/dev/tty
From the bash reference manual :
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt][-t timeout][-u fd] [name ...]
i'm using embedded linux here i need to set the current date to system which is based on user input given by the key board,here i'm failing at one condition that is the system has to wait for 2 minutes un till gets input from keyboard if it reaches to specified wait time it has to come out from loop.......
Below is my piece of code:
echo please enter the date in below format
echo YEAR- MM-DD HRS:MNS:SEC and press enter
read -e a1
startd=$(date -s "$a1");
echo "$startd";
hwclock --systohc
date
You should use option -t in your the read command:
if read -e -t 120 a1; then
echo "input: $a1"
else
echo "no input"
fi
From the bash man page:
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
...
-t timeout
Cause read to time out and return failure if a complete line of input (or a specified number of characters) is not read within timeout seconds.
I will like to write (using bash) something like
while no_user_key_pressed
{
do_something....
}
There are a few options using C++, Java, ncurses and others o/s specific. I want a simple bash portable function.
^c interrupt should be used to kill the remaining code. Imagine something like: 'Press any key to stop test'
You can use a small timeout on read -t.
The drawback is that the user must press < RETURN >, not "any key".
For example:
while ! read -t 0.01
do
echo -en "$(date)\r"
done
echo "User pressed: $REPLY"
Tested on bash 3.2 (OS X)
The ! is because read returns a failure (false) if the timeout expires.
You can trap Ctrl-c in a way that does not kill the remaining code:
$ cat test.sh
#!/usr/bin/env bash
trap 'break' INT
while true
do
date
sleep 1
done
echo done
$ ./test.sh
Tue 28 Jun 12:01:22 UTC 2016
Tue 28 Jun 12:01:23 UTC 2016
Tue 28 Jun 12:01:24 UTC 2016
^Cdone
I have a pretty simple bash script that sends command to serial and reads thee value back. The problem is when I don't get a value back,, it can get stuck.
echo BC > /dev/ttyS1
read line < /dev/ttyS1
echo $line
I have used the cat command with a timeout, but cannot use the same technique with 'read', because if I send the process to the background, I never get value back on exit. 'cat' works for the most part, but i'm not sure if this is the most robust way to do this.
echo BC > /dev/ttyS1
cat /dev/ttyS1 &
pid=$!
sleep 0.1
kill -9 $pid
From section 4.2 Bash Builtin Commands of the Bash Reference Manual:
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]
...
-t timeout
Cause read to time out and return failure if a complete line of input is not read within timeout seconds. timeout may be a decimal number with a fractional portion following the decimal point. This option is only effective if read is reading input from a terminal, pipe, or other special file; it has no effect when reading from regular files. If timeout is 0, read returns success if input is available on the specified file descriptor, failure otherwise. The exit status is greater than 128 if the timeout is exceeded.
I have a long running BASH script that I am running under CYGWIN on Windows.
I would like to limit the script to run for 30 seconds, and automatically terminate if it exceeds this limit. Ideally, I'd like to be able to do this to any command.
For example:
sh-3.2$ limittime -t 30 'myscript.sh'
or
sh-3.2$ limittime -t 30 'grep func *.c'
Under cygwin the ulimit command doesn't seem to work.
I am open to any ideas.
See the http://www.pixelbeat.org/scripts/timeout script the functionality of which has been integrated into newer coreutils:
#!/bin/sh
# Execute a command with a timeout
# License: LGPLv2
# Author:
# http://www.pixelbeat.org/
# Notes:
# Note there is a timeout command packaged with coreutils since v7.0
# If the timeout occurs the exit status is 124.
# There is an asynchronous (and buggy) equivalent of this
# script packaged with bash (under /usr/share/doc/ in my distro),
# which I only noticed after writing this.
# I noticed later again that there is a C equivalent of this packaged
# with satan by Wietse Venema, and copied to forensics by Dan Farmer.
# Changes:
# V1.0, Nov 3 2006, Initial release
# V1.1, Nov 20 2007, Brad Greenlee <brad#footle.org>
# Make more portable by using the 'CHLD'
# signal spec rather than 17.
# V1.3, Oct 29 2009, Ján Sáreník <jasan#x31.com>
# Even though this runs under dash,ksh etc.
# it doesn't actually timeout. So enforce bash for now.
# Also change exit on timeout from 128 to 124
# to match coreutils.
# V2.0, Oct 30 2009, Ján Sáreník <jasan#x31.com>
# Rewritten to cover compatibility with other
# Bourne shell implementations (pdksh, dash)
if [ "$#" -lt "2" ]; then
echo "Usage: `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi
cleanup()
{
trap - ALRM #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null && #kill last job
exit 124 #exit with 124 if it was running
}
watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}
watchit $1& a=$! #start the timeout
shift #first param was timeout for sleep
trap "cleanup" ALRM INT #cleanup after timeout
"$#"& wait $!; RET=$? #start the job wait for it and save its return value
kill -ALRM $a #send ALRM signal to watchit
wait $a #wait for watchit to finish cleanup
exit $RET #return the value
The following script shows how to do this using background tasks. The first section kills a 60-second process after the 10-second limit. The second attempts to kill a process that's already exited. Keep in mind that, if you set your timeout really high, the process IDs may roll over and you'll kill the wrong process but this is more of a theoretical issue - the timeout would have to be very large and you would have to be starting a lot of processes.
#!/usr/bin/bash
sleep 60 &
pid=$!
sleep 10
kill -9 $pid
sleep 3 &
pid=$!
sleep 10
kill -9 $pid
Here's the output on my Cygwin box:
$ ./limit10
./limit10: line 9: 4492 Killed sleep 60
./limit10: line 11: kill: (4560) - No such process
If you want to only wait until the process has finished, you need to enter a loop and check. This is slightly less accurate since sleep 1 and the other commands will actually take more than one second (but not much more). Use this script to replace the second section above (the "echo $proc" and "date" commands are for debugging, I wouldn't expect to have them in the final solution).
#!/usr/bin/bash
date
sleep 3 &
pid=$!
((lim = 10))
while [[ $lim -gt 0 ]] ; do
sleep 1
proc=$(ps -ef | awk -v pid=$pid '$2==pid{print}{}')
echo $proc
((lim = lim - 1))
if [[ -z "$proc" ]] ; then
((lim = -9))
fi
done
date
if [[ $lim -gt -9 ]] ; then
kill -9 $pid
fi
date
It basically loops, checking if the process is still running every second. If not, it exits the loop with a special value to not try and kill the child. Otherwise it times out and does kill the child.
Here's the output for a sleep 3:
Mon Feb 9 11:10:37 WADT 2009
pax 4268 2476 con 11:10:37 /usr/bin/sleep
pax 4268 2476 con 11:10:37 /usr/bin/sleep
Mon Feb 9 11:10:41 WADT 2009
Mon Feb 9 11:10:41 WADT 2009
and a sleep 60:
Mon Feb 9 11:11:51 WADT 2009
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
Mon Feb 9 11:12:03 WADT 2009
Mon Feb 9 11:12:03 WADT 2009
./limit10: line 20: 4176 Killed sleep 60
Check out this link. The idea is just that you would run myscript.sh as a subprocess of your script and record its PID, then kill it if it runs too long.
timeout 30s YOUR_COMMAND COMMAND_ARGUMENTS
Below are all the options for "timeout" under coreutils:
$ timeout --help
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.
Mandatory arguments to long options are mandatory for short options too.
--preserve-status
exit with the same status as COMMAND, even when the
command times out
--foreground
when not running timeout directly from a shell prompt,
allow COMMAND to read from the TTY and get TTY signals;
in this mode, children of COMMAND will not be timed out
-k, --kill-after=DURATION
also send a KILL signal if COMMAND is still running
this long after the initial signal was sent
-s, --signal=SIGNAL
specify the signal to be sent on timeout;
SIGNAL may be a name like 'HUP' or a number;
see 'kill -l' for a list of signals
--help display this help and exit
--version output version information and exit
DURATION is a floating point number with an optional suffix:
's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days.
If the command times out, and --preserve-status is not set, then exit with
status 124. Otherwise, exit with the status of COMMAND. If no signal
is specified, send the TERM signal upon timeout. The TERM signal kills
any process that does not block or catch that signal. It may be necessary
to use the KILL (9) signal, since this signal cannot be caught, in which
case the exit status is 128+9 rather than 124.
GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/timeout>
or available locally via: info '(coreutils) timeout invocation'
You could run the command as a background job (i.e. with "&"), use the bash variable for "pid of last command run," sleep for the requisite amount of time, then run kill with that pid.