Ler um caractere significa ler um byte de cada vez até obter um caractere completo.
Para ler um byte com o toolkit de POSIX, há dd bs=1 count=1
.
Note, no entanto, a leitura de um dispositivo terminal, quando esse dispositivo está no modo icanon
(como geralmente é por padrão), somente retorna quando você pressiona Return ( Enter ), porque até lá o driver do dispositivo de terminal implementa uma forma de editor de linhas que permite usar Backspace ou outros caracteres de edição para alterar o que você digita, e o que você digita é disponibilizado para o lendo o aplicativo somente quando você enviar a linha que você está editando (com Retornar ou Ctrl + D ).
Por esse motivo, ksh
' read -n/N
ou zsh
' read -k
, quando detectam stdin é um dispositivo terminal, coloque esse dispositivo fora do modo icanon
, para que os bytes estejam disponíveis para ler assim que forem enviados pelo terminal.
Agora observe que ksh
' read -n n
só lê up para n
caracteres de uma única linha , ele ainda para quando um caractere de nova linha é lido (use -N n
para ler n
caracteres). bash
, ao contrário do ksh93, ainda faz o processamento de IFS e backslash para -n
e -N
.
Para imitar zsh
ou read -k
ksh93
ou read -N1
bash
, ou seja, leia um e apenas um caractere de stdin, POSIXly:
readc() { # arg: <variable-name>
if [ -t 0 ]; then
# if stdin is a tty device, put it out of icanon, set min and
# time to sane value, but don't otherwise touch other input or
# or local settings (echo, isig, icrnl...). Take a backup of the
# previous settings beforehand.
saved_tty_settings=$(stty -g)
stty -icanon min 1 time 0
fi
eval "$1="
while
# read one byte, using a work around for the fact that command
# substitution strips the last character.
c=$(dd bs=1 count=1 2> /dev/null; echo .)
c=${c%.}
# break out of the loop on empty input (eof) or if a full character
# has been accumulated in the output variable (using "wc -m" to count
# the number of characters).
[ -n "$c" ] &&
eval "$1=\${$1}"'$c
[ "$(($(printf %s "${'"$1"'}" | wc -m)))" -eq 0 ]'; do
continue
done
if [ -t 0 ]; then
# restore settings saved earlier if stdin is a tty device.
stty "$saved_tty_settings"
fi
}