Einige Leute haben die falsche Vorstellung, dass read
es der Befehl ist, eine Zeile zu lesen. Es ist nicht.
read
Liest Wörter aus einer (möglicherweise mit Backslash fortgesetzten) Zeile, in der Wörter durch $IFS
Trennzeichen getrennt sind und Backslash verwendet werden kann, um die Trennzeichen zu umgehen (oder Zeilen fortzusetzen).
Die generische Syntax lautet:
read word1 word2... remaining_words
read
stdin liest ein Byte zu einer Zeit , bis er eine unescaped Zeilenende- Zeichen (oder End-of-Eingang) findet, aufteilt , dass das Ergebnis dieser Aufteilung in zu komplexen Regeln und speichert nach $word1
, $word2
... $remaining_words
.
Zum Beispiel bei einer Eingabe wie:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
und mit dem Standardwert $IFS
, read a b c
würde zuweisen:
$a
⇐ foo
$b
⇐ bar baz
$c
⇐ blah blahwhatever whatever
Nun, wenn nur ein Argument übergeben wird, wird das nicht read line
. Es ist immer noch so read remaining_words
. Die Verarbeitung von umgekehrten Schrägstrichen wird weiterhin durchgeführt. IFS-Leerzeichen werden weiterhin am Anfang und am Ende entfernt.
Die -r
Option entfernt die Backslash-Verarbeitung. Also würde derselbe Befehl wie oben mit -r
vergeben
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
Für den aufteilenden Teil ist es wichtig zu $IFS
wissen, dass es zwei Klassen von Zeichen gibt : die IFS-Whitespace-Zeichen (nämlich Leerzeichen und Tabulatoren (und Zeilenumbrüche, obwohl dies hier keine Rolle spielt, wenn Sie -d verwenden), die ebenfalls vorkommen im Standardwert von $IFS
) und den anderen sein. Die Behandlung dieser beiden Charakterklassen ist unterschiedlich.
Mit IFS=:
( :
wobei keine IFS Leerzeichen), wie ein Eingang :foo::bar::
in aufgeteilt werden würde ""
, "foo"
, ""
, bar
und ""
(und eine extra ""
mit einigen Implementierungen obwohl das nicht mit Ausnahme keine Rolle read -a
). Wenn wir dies durch :
Leerzeichen ersetzen, erfolgt die Aufteilung nur in foo
und bar
. Das heißt, führende und nachfolgende werden ignoriert, und Sequenzen von ihnen werden wie eine behandelt. Es gibt zusätzliche Regeln, wenn Leerzeichen und Nicht-Leerzeichen kombiniert werden $IFS
. Einige Implementierungen können die Sonderbehandlung durch Verdoppeln der Zeichen in IFS ( IFS=::
oder IFS=' '
) hinzufügen / entfernen .
Wenn wir also nicht möchten, dass die führenden und nachfolgenden Leerzeichen ohne Leerzeichen entfernt werden, müssen wir diese IFS-Leerzeichen aus IFS entfernen.
Selbst bei IFS-Zeichen ohne Leerzeichen wird diese Eingabe durchgeführt, wenn die Eingabezeile eines (und nur eines) dieser Zeichen enthält und es sich um das letzte Zeichen in der Zeile handelt (wie IFS=: read -r word
bei einer Eingabe wie foo:
), die POSIX-Shells enthält (nicht zsh
oder in einigen pdksh
Versionen) gilt als eine betrachtet foo
in diesen Schalen , weil Wort, die Zeichen $IFS
werden als als Terminatoren , so word
enthalten foo
, nicht foo:
.
Der kanonische Weg, eine Eingabezeile mit dem read
eingebauten Code zu lesen, ist:
IFS= read -r line
(Beachten Sie, dass dies bei den meisten read
Implementierungen nur für Textzeilen funktioniert, da das NUL-Zeichen nur in unterstützt wird. zsh
)
Durch var=value cmd
die Verwendung der Syntax wird sichergestellt, dass IFS
nur für die Dauer dieses cmd
Befehls ein anderer Wert festgelegt wird.
Geschichtsnotiz
Das read
Builtin wurde von der Bourne-Shell eingeführt und sollte schon Worte , keine Zeilen lesen . Es gibt einige wichtige Unterschiede zu modernen POSIX-Shells.
Die Bourne-Shell read
unterstützt keine -r
Option (die von der Korn-Shell eingeführt wurde), daher gibt es keine Möglichkeit, die Backslash-Verarbeitung zu deaktivieren, außer die Eingabe mit so etwas wie dieser sed 's/\\/&&/g'
vorzuverarbeiten.
Die Bourne-Shell hatte nicht die Vorstellung von zwei Klassen von Zeichen (die wiederum von ksh eingeführt wurde). In der Bourne - Shell alle Zeichen der gleichen Behandlung unterzogen werden, wie IFS Leerzeichen in KSH tun, ist , dass IFS=: read a b c
auf einem Eingangs wie foo::bar
zuweisen würde , bar
um $b
nicht den leeren String.
In der Bourne-Shell mit:
var=value cmd
Wenn cmd
es ein eingebautes ist (wie es read
ist), var
bleibt es auf eingestellt, value
nachdem cmd
es fertig ist. Das ist besonders kritisch, $IFS
da in der Bourne-Shell $IFS
alles aufgeteilt wird, nicht nur die Erweiterungen. Wenn Sie das Leerzeichen $IFS
in der Bourne-Shell entfernen , "$@"
funktioniert dies ebenfalls nicht mehr.
In der Bourne-Shell führt das Umleiten eines zusammengesetzten Befehls dazu, dass dieser in einer Subshell ausgeführt wird (in den frühesten Versionen funktionierten sogar Dinge wie read var < file
oder exec 3< file; read var <&3
funktionierten nicht). In der Bourne-Shell war es daher selten, read
etwas anderes als Benutzereingaben auf dem Terminal zu verwenden (wo diese Zeilenfortsetzungsbehandlung Sinn machte)
Einige Unices (wie HP / UX, es gibt auch einen in util-linux
) haben noch einen line
Befehl zum Lesen einer Eingabezeile (der bis zur Single UNIX Specification Version 2 ein Standard-UNIX-Befehl war ).
Das ist im Grunde dasselbe, mit der head -n 1
Ausnahme, dass jeweils ein Byte gelesen wird, um sicherzustellen, dass nicht mehr als eine Zeile gelesen wird. Auf diesen Systemen können Sie Folgendes ausführen:
line=`line`
Das bedeutet natürlich, einen neuen Prozess zu erzeugen, einen Befehl auszuführen und seine Ausgabe über eine Pipe zu lesen IFS= read -r line
, was viel weniger effizient ist als die von ksh , aber dennoch viel intuitiver.