local stty="$(stty -g)"
Save the current terminal settings. stty $stty
, which is executed both when the function returns normally and on SIGINT, restores these settings.
trap "stty $stty; trap SIGINT; return 128" SIGINT
If the function is interrupted by SIGINT (the signal sent by pressing Ctrl+C), restore the terminal settings and return 128. (Why 128? I wonder. Normally the exit status on a signal would be 128 + signal number.)
stty cbreak -echo
Disable the terminal's crude editing functionality (character/word/line erase), and turn off the echo of characters as they are typed.
key=$(dd count=1 2>/dev/null) || return $?
Read up to 512 bytes from the terminal (count
is a number of blocks, and the default block size is 512 bytes). This is a bit strange: I think the intent was to read one byte. Since dd
will return as soon as at least one byte is available, this will return a single byte in practice if a user is typing, but if a program is feeding keystrokes or if the system is slow, this could read more bytes. The code has the benefit that if the user types a multibyte character, all the bytes that make up the character are likely (but not guaranteed) to be read in the loop iteration.
If dd
returns a nonzero status, this indicates a read error or a signal; the function returns immediately. The terminal settings are not restored, which is a bug, though most of the time the error would be either that the user pressed Ctrl+C, in which case the terminal settings are restored, or that the terminal has disappeared, in which case the point is moot.
if [ -z "$1" ] || [[ "$key" == [$1] ]]; then
break
fi
Exit the loop if the byte(s) that was read is one of the characters in the argument to the function. If the argument is empty, any character terminates the loop. The argument isn't exactly a list of characters, it's in wildcard character set syntax: an initial ^
or !
inverts the set, a minus sign in most positions is parsed as a range (e.g. 0-9
), [:…:]
and [.….]
denote character classes and collating symbols respectively, and a backslash quotes the next \
, [
, ]
or -
.