Lesen Sie -a Array -d '\ n' <foo, beenden Sie Code 1


8

Wenn ich versuche auszuführen

read -a fooArr -d '\n' < bar

Der Exit-Code ist 1 - obwohl er das erreicht, was ich will. Fügen Sie jede Zeile barin ein Element des Arrays ein fooArr(mit bash 4.2.37).

Kann jemand erklären, warum das passiert


Ich habe andere Wege gefunden, um dies zu lösen, wie die folgenden, also frage ich nicht danach.

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar

oder

mapfile -t fooArr < bar

Antworten:


14

Was erklärt werden muss, ist, dass der Befehl zu funktionieren schien, nicht sein Exit-Code

'\n'besteht aus zwei Zeichen: einem Backslash \und einem Buchstaben n. Was Sie für nötig hielten, war $'\n'ein Zeilenvorschub (aber das wäre auch nicht richtig, siehe unten).

Die -dOption macht dies:

  -d delim  continue until the first character of DELIM is read, rather
            than newline

Ohne diese Option readwürde ich also bis zu einer neuen Zeile lesen, die Zeile in Wörter aufteilen, wobei die Zeichen $IFSals Trennzeichen verwendet werden, und die Wörter in das Array einfügen . Wenn Sie angeben -d $'\n', dass das Zeilentrennzeichen auf eine neue Zeile gesetzt wird, würde es genau dasselbe tun . Einstellung -d '\n'bedeutet, dass bis zum ersten Backslash gelesen wird (siehe jedoch unten), bei dem es sich um das erste Zeichen in handelt delim. Da Ihre Datei keinen Backslash enthält, readwird der am Ende der Datei beendet und:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.

Deshalb ist der Exit-Code 1.

Aus der Tatsache, dass Sie glauben, dass der Befehl funktioniert hat, können wir schließen, dass die Datei keine Leerzeichen enthält, sodass sie readnach dem Lesen der gesamten Datei in der vergeblichen Hoffnung, einen Backslash zu finden, durch Leerzeichen (den Standardwert von) aufgeteilt wird $IFS), einschließlich Zeilenumbrüche. Daher wird jede Zeile (oder jedes Wort, wenn eine Zeile mehr als ein Wort enthält) im Array gespeichert.

Der mysteriöse Fall des entwendeten Rückschlags

Woher wusste ich, dass die Datei keine Backslashes enthält? Weil Sie die -rFlagge nicht geliefert haben an read:

  -r                do not allow backslashes to escape any characters

Wenn Sie also Backslashes in der Datei gehabt hätten, wären diese entfernt worden, es sei denn, Sie hätten zwei davon hintereinander. Und natürlich gibt es Beweise, readdie einen Exit-Code von 1 hatten, was zeigt, dass kein Backslash gefunden wurde, also gab es auch nicht zwei hintereinander.

Imbissbuden

Bash wäre keine Bash, wenn sich nicht hinter fast jedem Befehl Fallstricke verstecken würden, und das readist keine Ausnahme. Hier sind ein paar:

  1. Sofern Sie nichts anderes angeben -r, readwerden Backslash-Escape-Sequenzen interpretiert. Sofern dies nicht tatsächlich das ist, was Sie möchten (was gelegentlich, aber nur gelegentlich -rder Fall ist ), sollten Sie dies angeben , um zu vermeiden, dass Zeichen in dem seltenen Fall verschwinden, dass die Eingabe Backslashes enthält.

  2. Die Tatsache, dass readein Exit-Code von 1 zurückgegeben wird, bedeutet nicht, dass er fehlgeschlagen ist. Es könnte durchaus gelungen sein, außer den Leitungsabschluss zu finden. Seien Sie also vorsichtig mit einer Schleife wie folgt aus : while read -r LINE; do something with LINE; done weil es zum Scheitern verurteilt wird do somethingmit der letzten Zeile in dem seltenen Fall , dass die letzte Zeile nicht eine neue Zeile am Ende hat.

  3. read -r LINE Bewahrt Backslashes, aber keine führenden oder nachfolgenden Leerzeichen.


Vielen Dank! Ich dachte, ich hätte den $ '\ n'-Ansatz ausprobiert, aber ich denke nicht: / Schön zu wissen, dass -r
RasmusWL

2
"Der seltene Fall, dass die letzte Zeile keine neue Zeile hatte" scheint mir kein zwingender Grund zu sein, "niemals verwenden while read" zu empfehlen . Ansonsten fantastische Antwort.
Glenn Jackman

@glennjackman: Sie können verwenden while read, solange Sie nichts in der Schleife haben. Ansonsten ist es ein Käfer, der darauf wartet, dich zu beißen - und glaub mir, ich bin gebissen worden.
Rici

2
Die letzte Zeile hat immer eine neue Zeile. So wird eine Zeile definiert: Sie endet mit einer neuen Zeile. Wenn eine nicht leere Datei nicht mit einem Zeilenumbruch endet, handelt es sich nicht um eine Textdatei, und Sie können keine Textverarbeitungswerkzeuge wie das Shell-Dienstprogramm verwenden read.
Gilles 'SO - hör auf böse zu sein'

2
@glennjackman: Ich iteriere meistens mit awk durch kolumnierte Dateien. Das Durchlaufen ganzer Zeilen, bei denen die Datei nicht zu groß ist, mapfileist ziemlich cool. Für einen schnellen und schmutzigen Hack verwende ich die verbotene while-Schleife, aber ich habe aufgehört, sie in Produktionsskripte einzufügen. YMMV und ich haben die Warnung in der Antwort gemildert.
Rici

4

Das ist das erwartete Verhalten:

Der Rückkehrcode ist Null, es sei denn, das Dateiende ist erreicht, [...]

start cmd:> echo a b c | { read -a testarray; echo $?; }
0

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.