16

So, I want to write a bash script that are a sequence of steps and ill identify it as "task#". However, each step is only completed and can run as long as the user wants.

Do task1
if keypressed stop task1 and move on #this is the part I need help with. There can be up to 10 of these move on steps. 
Do task2
...

kina like top; it keeps doing stuff until you hit q to quite, however, i want to move on to the next thing

Cripto
  • 3,581
  • 7
  • 41
  • 65

2 Answers2

17

you can use read builtin command with option -t and -n

while :
do
    # TASK 1
    date
    read -t 1 -n 1 key

    if [[ $key = q ]]
    then
        break
    fi
done

# TASK 2
date +%s
kev
  • 155,172
  • 47
  • 273
  • 272
  • 1
    @jarno: I appreciate the correction and the pointers. I've deleted my original comment and decided to dig deeper in my own answer. – mklement0 Jun 25 '17 at 16:28
5

kev's great solution works well even in Bash 3.x., but it introduces a 1-second delay (-t 1) in every loop iteration.

In Bash 3.x, the lowest supported value for -t (timeout) is 1 (second), unfortunately.

Bash 4.x supports 0 and fractional values, however:

A solution that supports an arbitrary key such as q requires a nonzero -t value, but you can specify a value very close to 0 to minimize the delay:

#!/bin/bash
# !! BASH 4.x+ ONLY

while :; do

  # Loop command
  date

  # Check for 'q' keypress *waiting very briefly*  and exit the loop, if found.
  read -t 0.01 -r -s -N 1 && [[ $REPLY == 'q' ]] && break

done

# Post-loop command
date +%s

Caveat: The above uses 0.01 as the almost-no-timeout value, but, depending on your host platform, terminal program and possibly CPU speed/configuration, a larger value may be required / a smaller value may be supported. If the value is too small, you'll see intermittent error setting terminal attributes: Interrupted system call errors - if anyone knows why, do tell us.


Tip of the hat to jarno for his help with the following:

Using -t 0, works as follows, according to help read (emphasis added):

If TIMEOUT is 0, read returns immediately, without trying to read any data, returning success only if input is available on the specified file descriptor.

As of Bash v4.4.12 and 5.0.11, unfortunately, -t 0 seems to ignore -n / -N, so that only an ENTER keypress (or a sequence of keypresses ending in ENTER) causes read to indicate that data is available.
If anyone knows whether this is a bug or whether there's a good reason for this behavior, do let us know.

Therefore, only with ENTER as the quit key is a -t 0 solution currently possible:

#!/bin/bash
# !! BASH 4.x+ ONLY

while :; do

  # Loop command
  date

  # Check for ENTER keypress and, after clearing the input buffer
  # with a dummy `read`, exit the loop.
  read -t 0 -r -N 1 && { read -r; break; }

done

# Post-loop command
date +%s
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    getting search hit here for and researching error of **setting terminal attributes** from `bash -o monitor -c 'read -t1 -n1 -s -pp: r &'` (NB `-o monitor` for `&`), I found the error comes from builtins/read.def calling sh_ttyerror(1) in builtins/common.c when some calls matched by `grep -EHn 'tt(setattr|_set(cbreak|onechar|noecho))'` in [lib/sh/shtty.c](http://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/shtty.c) fail. (Removing the `-s` prevents one error.) Adding `-e` for using `readline` prevents any errors for me. – vike Oct 26 '19 at 04:57
  • Thanks, @vike. Based on your findings, is there a way to make the above work with `-t 0`? – mklement0 Oct 26 '19 at 12:18
  • 1
    no, the info on `-t0` checks out (as special case in read.def, independent of other opts). Though, I did find my **bash 5.0.7** seem to support `-t0.001` with only `-n1`, but `-t0.0001` with only `-N1` (independent of `-s` and/or `-r`). Oddly, `-t0.00001` as only opt seem supported, but with `-e` only `-t0.002233` does. These fractions could depend on local cpu times as `setitimer`(3) rounds to "resolution of the system clock" ("typically 10 milliseconds" in my '93 manpage), is called with ITIMER_REAL, and a CHECK_ALRM macro (from quit.h) is used at different places in read.def. – vike Oct 28 '19 at 03:29
  • Thanks for digging deeper, @vike. Given how much research you've done already, I encourage you to submit a detailed bug report re `-t 0` (e.g. via the `bashbug` script). – mklement0 Oct 28 '19 at 12:19