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 ?zsh
. Für den POSIX-Ansatz können Sie sich im Fall 012 um Newline kümmern.-icannon
einemctrl-s
(als ein Beispiel für nicht verwaltete Eingabe) wird der Code ausgesetzt. Dadurch wird die TTY blockiert, bis actrl-q
ausgegeben wird. Es gibt mehrere andere Schlüssel, die nicht gelesen werden, aber die tty beeinflussen, als zusätzliches Beispielctrl-C
.printf "\\$code"
,printf '%s' "\\$code"
da der Wert von$code
alles sein kann, wenn der-t
Test fehlschlägt.REPLY=$REPLY……
wenn die Funktion sowieso ein Zeichen liest?Im
f()
Wechsel das%s
zu%q
:Ausgabe, wenn der Benutzer eine neue Zeile eingibt , dann ' Strg-D ':
Von `man printf:
quelle
You entered '$'\012''
dem aktuell angezeigten Nullzeichen anzeigen ?-d ''
f() { read -rd '' -n1 -p "Enter a character: " char && printf "\nYou entered: %q\n" "$char"; }
Ich habe dazu eine separate Frage gestellt .Wenn Sie
read -rn1
in Bash laufen und drücken^D
, wird dies als wörtliches Steuerzeichen und nicht als EOF-Bedingung behandelt. Das Steuerzeichen ist beim Drucken einfach nicht sichtbar und wird daher nicht mit angezeigtprintf "'%s'"
. Das Weiterleiten der Ausgabe an etwas wieod -c
würde es zeigen, ebenso wieprintf "%q"
die anderen bereits erwähnten Antworten.Mit eigentlich nichts als Eingabe ist das Ergebnis anders, hier sogar leer mit
printf "%q"
:Die neue Zeile wird
read
hier aus zwei Gründen nicht zurückgegeben . Erstens ist es das Standardzeilentrennzeichen für Lesen und wird daher als Ausgabe zurückgegeben. Zweitens ist es auch Teil der StandardeinstellungIFS
undread
entfernt führende und nachfolgende Leerzeichen, wenn sie Teil von sindIFS
.Wir müssen also
read -d
das Trennzeichen vom Standard ändern undIFS
leer machen :read -d ""
macht das Trennzeichen effektiv zum NUL-Byte, was bedeutet, dass dies immer noch nicht den Unterschied zwischen einer Eingabe von nichts und einer Eingabe eines NUL-Bytes erkennt:Obwohl nichts als Eingabe verwendet wird, wird
read
false zurückgegeben, sodass wir dies überprüfen$?
können.quelle
Die Fälle newline und Strg-D werden durch die Statusvariable unterschieden.
Bei Zeilenumbrüchen ist der Status wahr (0), während bei Strg-D der Status falsch ist (1).
quelle
-n 1
. Der Status ist 0.-n 1
und Status 0 zeigt ein "\ n" an, das auf mysteriöse Weise entfernt wird .