How to do the following?
- I have a loop script.
- I want to stop it when i press a key,otherwise it will continue in 5 seconds.
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.Sign up to join this community
How to do the following?
Something to bear in mind is that shells or any applications that you typically run in a terminal don't interact with a keyboard and screen.
They take input from their stdin as a stream of bytes. When that stdin comes from a regular file, the bytes come from there, when it's a pipe, that's data typically sent by another process, when it's some device file that can come to physical devices attached to the computer. For instance, when it's a tty character device, that's data sent over some serial line typically by a terminal. A terminal is some form of device that transforms keyboard events into sequences of bytes.
That's where the whole power of terminal applications resides. The input mechanism has been abstracted for them, so they can be used interactively or automatically in scripts.
Here, if you're going to issue this kind of prompt and expecting a key press event, you probably want your application (your script) to be an interactive one only. Either expect stdin to be a terminal, or take input from the terminal regardless of what stdin is open on.
Now, as seen above, all applications see are streams of bytes, that's the terminal's (or terminal emulator) and the tty device line discipline's role to translate key pressed into sequences of bytes. A few examples:
xterm, on ASCII-based systems, will either send 0x1b 0x4f 0x44 or 0x1b 0x5b 0x44 (
So here, the questions I'd ask would be:
Here, I assume you want your script to be a terminal interactive application only and interact only through the controlling terminal leaving stdin/stdout alone.
#! /bin/sh - # ":" being a special builtin, POSIX requires it to exit if a # redirection fails, which makes this a way to easily check if a # controlling terminal is present and readable: :</dev/tty # if using bash however not in POSIX conformance mode, you'll need to # change it to something like: exec 3< /dev/tty 3<&- || exit read_key_with_timeout() ( timeout=$1 prompt=$2 saved_tty_settings=$(stty -g) || exit # if we're killed, restore the tty settings, the convoluted part about # killing the subshell process is to work around a problem in shells # like bash that ignore a SIGINT if the current command being run handles # it. for sig in INT TERM QUIT; do trap ' stty "$saved_tty_settings" trap - '"$sig"' pid=$(exec sh -c '\''echo "$PPID"'\'') kill -s '"$sig"' "$pid" # fall back if kill failed above exit 2' "$sig" done # drain the tty's buffer stty -icanon min 0 time 0; cat > /dev/null printf '%s\n' "$prompt" # use the tty line discipline features to say the next read() # should wait at most the given number of deciseconds (limited to 255) stty time "$((timeout * 10))" -echo # do one read and count the bytes returned count=$(dd 2> /dev/null count=1 | wc -c) # If the user pressed a key like the £ or Home ones described above # it's likely all the corresponding bytes will have been read by dd # above, but not guaranteed, so we may want to drain the tty buffer # again to make sure we don't leave part of the sequence sent by a # key press to be read by the next thing that reads from the tty device # thereafter. Here allowing the terminal to send bytes as slow as 10 # per second. Doing so however, we may end up reading the bytes sent # upon subsequent key presses though. stty time 1; cat > /dev/null stty "$saved_tty_settings" # return whether at least one byte was read: [ "$(($count))" -gt 0 ] ) <> /dev/tty >&0 2>&0 until echo "Hello World" sleep 1 echo "Done greeting the world" read_key_with_timeout 5 "Press any key to stop" do continue done
while true; do echo 'Looping, press Ctrl+C to exit' sleep 5 done
There is no need to make it more complicated than that.
The following requires
while true; do echo 'Press any key to exit, or wait 5 seconds' if read -r -N 1 -t 5; then break fi done
read fails (by timing out), the loop continues.