2015-04-17 20 views
18

I Datei a.txt mit Inhalt folgendenBash Leseleitung nicht lesen führende Leerzeichen

aaa 
    bbb 

Wenn ich folgende Skript ausführen:

while read line 
do 
    echo $line 
done <a.txt> b.txt 

erzeugt b.txt enthält folgende

aaa 
bbb 

Es Man sieht, dass die führenden Linien der Linien entfernt sind. Wie kann ich führende Räume erhalten?

Antwort

27

Dies ist in dem Bash FAQ-Eintrag auf reading data line-by-line bedeckt.

Der Lesebefehl ändert jede gelesene Zeile; Standardmäßig werden alle führenden und nachfolgenden Leerzeichen (Leerzeichen und Tabulatoren oder Leerzeichen in IFS) entfernt. Wenn das nicht gewünscht ist, muss die IFS-Variable gelöscht werden:

# Exact lines, no trimming 
while IFS= read -r line; do 
    printf '%s\n' "$line" 
done < "$file" 

Als Charles Duffy zu Recht ausführt (und ich hatte durch die Konzentration auf die IFS Ausgabe verpasst); Wenn Sie die Leerzeichen in Ihrer Ausgabe sehen wollen, müssen Sie auch die Variable zitieren, wenn Sie sie verwenden, oder die Shell wird wieder das Leerzeichen löschen.

Hinweise zu einigen anderen Unterschieden in diesem zitierten Snippet im Vergleich zu Ihrem ursprünglichen Code.

Die Verwendung des Arguments -r zu read wird in einem einzigen Satz oben auf der zuvor verknüpften Seite behandelt.

Die Option -r zum Lesen verhindert die umgekehrte Schrägstrich-Interpretation (wird normalerweise als Backslash-Zeilenumbruchpaar verwendet, um über mehrere Zeilen hinweg fortgesetzt zu werden). Ohne diese Option werden alle umgekehrten Schrägstriche in der Eingabe verworfen. Sie sollten fast immer die Option -r beim Lesen verwenden.

Als printf statt echo es das Verhalten von echo zu verwenden ist, etwas leider nicht portabel konsistent in allen Umgebungen und die Unterschiede umständlich sein kann, zu behandeln. printf ist dagegen konsistent und kann völlig robust eingesetzt werden.

+5

Wenn Sie keine 'read'-Argumente angeben, um die Eingabe zu halten (die Standardvariable' REPLY' wird verwendet), wird kein Leerzeichen entfernt und Sie können die Änderung an 'IFS' weglassen. Das heißt, während Read -r; tue printf '% s \ n' "$ REPLY"; done <"$ file" ' – chepner

+1

@chepner Interessant. Ich frage mich, warum das so ist. –

+2

Ich bin mir nicht sicher; Soweit ich weiß, scheint es nicht dokumentiert zu sein. Es macht einen Sinn, wenn Sie daran denken, dass Zero-Argumente erfordern, dass die Zeile in Nullfelder aufgeteilt wird, was bedeutet, dass es für "IFS" keine Verwendung gibt. (Das setzt voraus, dass Sie akzeptieren, dass das Aufspalten einer Linie in ein Feld immer noch ein Split ist, wenn auch ein degenerierter.) In jedem Fall ist es ein 'Bash'ism; POSIX 'read' benötigt mindestens ein Argument. – chepner

9

Es gibt mehrere Probleme hier:

  • Sofern IFS gelöscht, read Streifen führende und nachfolgende Leerzeichen.
  • echo $line string-splits und glob-expandiert den Inhalt von $line, zerlegt es in einzelne Wörter und übergibt diese Wörter als einzelne Argumente an den echo Befehl. Selbst mit IFS, das unter read gelöscht wurde, würde echo $line weiterhin führende und nachfolgende Leerzeichen löschen und Läufe von Leerzeichen zwischen Wörtern in jeweils ein einzelnes Leerzeichen ändern. Darüber hinaus würde eine Zeile, die nur das Zeichen * enthält, um eine Liste von Dateinamen erweitert werden.
  • echo "$line" ist eine signifikante Verbesserung, wird aber immer noch nicht korrekt behandelt Werte wie -n, die es als Echo-Argument selbst behandelt. printf '%s\n' "$line" würde dies vollständig beheben.
  • read ohne -r behandelt Backslashes als Fortsetzungszeichen und nicht als wörtlichen Inhalt, so dass sie nicht in den Werten enthalten sind, die erzeugt werden, es sei denn, sie werden verdoppelt, um sich zu entziehen.

So:

while IFS= read -r line; do 
    printf '%s\n' "$line" 
done 
+0

Guter Rat, aber die Zwei-Zeichen-Sequenz '\ n' _nicht_ in einem _newline_ führt, kommt es zu _literal 'n'_. Im Gegensatz dazu bewirkt eine "\" -geschlossene _actual_ newline, dass "read" die Zeile _following_ auch liest und direkt an die aktuelle Zeile anfügt (wobei "\" und "newline" verworfen werden). Ein '\ 'vor jedem anderen Zeichen wird einfach verworfen. – mklement0

+2

Eine weitere Möglichkeit, das Verhalten von 'read' der Beschreibung ohne' -r': der Eingang in der gleichen Weise wird durch die (POSIX) Schale selbst (zB als Teil der geparsten einen Bareword mit individuell '\' -escaped Zeichen analysiert wird, eine Argumentliste), wie in 'read' die POSIX-Spezifikation unter http bei http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01 und im wesentlichen dupliziert beschrieben: //pubs.opengroup. org/onlinepubs/9699919799/Dienstprogramme/read.html. – mklement0

+2

Vielen Dank - ich werde das Quellmaterial überprüfen, um festzustellen, wie ich diesen Teil meiner Antwort am besten überarbeiten kann. –

Verwandte Themen