Mit read -n "$n"
(keine POSIX-Funktion) und wenn stdin ein Endgerät ist, wird read
das Terminal aus dem icanon
Modus versetzt, da sonst read
nur vollständige Zeilen angezeigt werden, die vom internen Zeileneditor der Terminalleitungsdisziplin zurückgegeben werden, und dann jeweils ein Byte gelesen werden, bis $n
Zeichen oder eine neue Zeile wurden gelesen (möglicherweise werden unerwartete Ergebnisse angezeigt, wenn ungültige Zeichen eingegeben werden).
Es liest bis zu $n
Zeichen aus einer Zeile. Sie müssen auch leer sein, $IFS
damit keine IFS-Zeichen von der Eingabe entfernt werden.
Da wir den icanon
Modus verlassen , ^D
ist das nichts Besonderes mehr. Wenn Sie also drücken Ctrl+D, wird das ^D
Zeichen gelesen.
Sie würden eof nicht vom Endgerät sehen, wenn das Terminal nicht irgendwie getrennt wäre. Wenn stdin ein anderer Dateityp ist, wird möglicherweise eof angezeigt (z. B. : | IFS= read -rn 1; echo "$?"
wenn stdin eine leere Pipe ist oder wenn stdin von umgeleitet wird /dev/null
).
read
gibt 0 zurück, wenn $n
Zeichen (Bytes, die nicht Teil gültiger Zeichen sind, die als 1 Zeichen gezählt werden) oder eine vollständige Zeile gelesen wurden.
In dem speziellen Fall, dass nur ein Zeichen angefordert wird:
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
POSIXly zu machen ist ziemlich kompliziert.
Das wäre ungefähr so (unter der Annahme eines ASCII-basierten Systems (im Gegensatz zu beispielsweise EBCDIC)):
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"
}
Beachten Sie, dass wir nur zurückkehren, wenn ein vollständiges Zeichen gelesen wurde. Wenn sich die Eingabe in der falschen Codierung befindet (anders als die Codierung des Gebietsschemas), z. B. wenn Ihr Terminal é
in iso8859-1 (0xe9) codiert sendet , wenn wir UTF-8 (0xc3 0xa9) erwarten, können Sie so viele eingeben, é
wie Sie möchten wird die Funktion nicht zurückkehren. bash
's read -n1
würde beim zweiten 0xe9 zurückkehren (und beide in der Variablen speichern), was ein etwas besseres Verhalten ist.
Wenn Sie auch ein ^C
Zeichen lesen möchten Ctrl+C(anstatt es Ihr Skript töten zu lassen; auch für ^Z
, ^\
...) oder
^S
/ ^Q
auf Ctrl+S/Q(anstelle der Flusskontrolle), können Sie -isig -ixon
der stty
Zeile ein hinzufügen . Beachten Sie, dass bash
‚s read -n1
es nicht tut entweder (es stellt auch isig
wenn es aus).
Dadurch werden die tty-Einstellungen nicht wiederhergestellt, wenn das Skript beendet wird (z. B. wenn Sie drücken Ctrl+C. Sie könnten ein hinzufügen trap
, dies würde jedoch möglicherweise andere trap
s im Skript überschreiben .
Sie können auch verwenden , zsh
statt bash
, wo read -k
(die früher ksh93
oder bash
‚s read -n/-N
) liest ein Zeichen aus dem Terminal und Griffe ^D
speziell für sich allein (kehrt nicht Null , wenn das Zeichen eingegeben wird ) und nicht behandeln Newline.
if read -k k; then
printf '1 character entered: %q\n' $k
fi
^D
, warum können wir\n
dann nicht erfassen ?