Com read -n "$n"
(não um recurso POSIX), e se stdin for um dispositivo terminal, read
colocará o terminal fora do modo icanon
, caso contrário read
verá somente linhas completas como retornadas pelo linha terminal Editor de linha interna da disciplina e, em seguida, lê um byte de cada vez até que $n
caracteres ou uma nova linha tenham sido lidos (você poderá ver resultados inesperados se forem inseridos caracteres inválidos).
Ele lê o caractere $n
de uma linha. Você também precisará esvaziar $IFS
para não remover caracteres IFS da entrada.
Como deixamos o modo icanon
, ^D
não é mais especial. Então, se você pressionar Ctrl + D , o caractere ^D
será lido.
Você não veria eof do dispositivo terminal a menos que o terminal seja de alguma forma desconectado. Se stdin for outro tipo de arquivo, você poderá ver eof (como em : | IFS= read -rn 1; echo "$?"
, em que stdin é um pipe vazio ou com o redirecionamento de stdin de /dev/null
)
read
retornará 0 se $n
caracteres (bytes não fazendo parte de caracteres válidos sendo contados como 1 caractere) ou uma linha completa tiver sido lida.
Assim, no caso especial de apenas um caractere sendo solicitado:
if IFS= read -rn 1 var; then
if [ "${#var}" -eq 0 ]; then
echo an empty line was read
else
printf %s "${#var} character "
(export LC_ALL=C; printf '%s\n' "made of ${#var} byte(s) was read")
fi
else
echo "EOF found"
fi
Fazê-lo POSIXly é bastante complicado.
Isso seria algo como (supondo um sistema baseado em ASCII (em oposição ao EBCDIC, por exemplo)):
readk() {
REPLY= ret=1
if [ -t 0 ]; then
saved_settings=$(stty -g)
stty -icanon min 1 time 0 icrnl
fi
while true; do
code=$(dd bs=1 count=1 2> /dev/null | od -An -vto1 | tr -cd 0-7)
[ -n "$code" ] || break
case $code in
000 | 012) ret=0; break;; # can't store NUL in variable anyway
(*) REPLY=$REPLY$(printf "\$code");;
esac
if expr " $REPLY" : ' .' > /dev/null; then
ret=0
break
fi
done
if [ -t 0 ]; then
stty "$saved_settings"
fi
return "$ret"
}
Note que retornamos somente quando um caractere completo foi lido. Se a entrada estiver na codificação incorreta (diferente da codificação da localidade), por exemplo, se o seu terminal enviar é
codificado em iso8859-1 (0xe9) quando esperamos UTF-8 (0xc3 0xa9), você pode digitar quantas é
como você gosta, a função não retornará. bash
read -n1
retornaria no segundo 0xe9 (e armazenaria ambos na variável), o que é um comportamento um pouco melhor.
Se você também quisesse ler um caractere ^C
no Ctrl + C (em vez de deixá-lo matar seu script; também para ^Z
, ^\
...), ou
^S
/ ^Q
em Ctrl + S / Q (em vez de controle de fluxo), você pode adicionar um -isig -ixon
à linha stty
. Observe que bash
read -n1
também não o faz (restaura isig
se estiver desativado).
Isso não restaurará as configurações tty se o script for eliminado (como se você pressionasse Ctrl + C . Você poderia adicionar um trap
, mas isso potencialmente substituiria outros trap
s em o script.
Você também pode usar zsh
em vez de bash
, onde read -k
(que é anterior a ksh93
ou bash
' read -n/-N
) lê um caractere do terminal e manipula ^D
sozinho ( retorna não-zero se esse caractere for inserido) e não trata a nova linha especialmente.
if read -k k; then
printf '1 character entered: %q\n' $k
fi