it-swarm.com.de

Das Lesen von Eingabedateien mit dem Befehl read in Shell-Scripting überspringt die letzte Zeile

Normalerweise verwende ich den Befehl read, um eine Eingabedatei Zeile für Zeile in das Shell-Skript zu lesen. Ein Beispielcode wie der folgende Code führt zu einem falschen Ergebnis, wenn am Ende der letzten Zeile der Eingabedatei (blah.txt) keine neue Zeile eingefügt wird.

#!/bin/sh

while read line
do
echo $line
done <blah.txt

Wenn also die Eingabedatei etwas liest -

One 
Two
Three
Four

und ich drücke nicht auf 'return', das Skript liest die letzte Zeile nicht und druckt 

One
Two
Three

Wenn ich jetzt nach vier eine zusätzliche leere Zeile hinterlasse,

One 
Two
Three
Four
//blank line

die Ausgabe druckt alle Zeilen, einschließlich Vier. Dies ist jedoch nicht der Fall, wenn ich eine Zeile mit dem Befehl cat lese. Alle Zeilen einschließlich der letzten werden gedruckt, ohne dass ich am Ende eine zusätzliche leere Zeile hinzufügen muss.

Hat jemand eine Idee, warum dies passiert? Die von mir erstellten Skripts werden größtenteils von anderen ausgeführt. Sie müssen daher am Ende jeder Eingabedatei keine zusätzliche Leerzeile hinzufügen.

Ich habe seit Ewigkeiten versucht, das herauszufinden. Ich wäre Ihnen dankbar, wenn Sie Lösungen haben (natürlich ist der Befehl cat einer, aber ich würde gerne wissen, aus welchem ​​Grund Lesen nicht so gut funktioniert.).

18
Caife

read liest, bis ein Zeilenvorschubzeichen oder das Ende der Datei gefunden wird, und gibt einen Exit-Code ungleich Null zurück, wenn er auf ein Dateiende stößt. Es ist also durchaus möglich, dass er sowohl eine Zeile liest als auch einen Exit-Code ungleich Null zurückgibt.

Folglich ist der folgende Code nicht sicher, wenn die Eingabe möglicherweise nicht durch eine neue Zeile abgeschlossen wird:

while read LINE; do
  # do something with LINE
done

weil der Rumpf der while in der letzten Zeile nicht ausgeführt wird.

Technisch gesehen ist eine Datei, die nicht mit einem Zeilenvorschub abgeschlossen ist, keine Textdatei, und Textwerkzeuge können bei einer solchen Datei auf seltsame Weise versagen. Ich zögere jedoch immer, auf diese Erklärung zurückzugreifen.

Eine Möglichkeit, das Problem zu lösen, besteht darin, zu testen, ob das Gelesene nicht leer ist (-n):

while read -r LINE || [[ -n $LINE ]]; do
  # do something with LINE
done

Andere Lösungen umfassen die Verwendung von mapfile zum Einlesen der Datei in ein Array, das Pipe der Datei durch ein Dienstprogramm, das garantiert, dass die letzte Zeile ordnungsgemäß beendet wird (grep ., wenn Sie z. B. keine Leerzeilen verwenden möchten) iterative Verarbeitung mit einem Werkzeug wie awk (was normalerweise meine Präferenz ist).

Beachten Sie, dass -r im read builtin fast sicher benötigt wird. es bewirkt, dass read\- Sequenzen in der Eingabe nicht neu interpretiert.

19
rici
8
Steven Penny

Eine Zeile Antwort:

IFS=$'\n'; for line in $(cat file.txt); do echo "$line" ; done
4
sandino

Verwenden Sie die while-Schleife wie folgt:

while IFS= read -r line || [ -n "$line" ]; do
  echo "$line"
done <file

Oder verwenden Sie grep mit while-Schleife:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)

Bei Verwendung von grep . anstelle von grep "" werden die leeren Zeilen übersprungen.

Hinweis: 

  1. Bei Verwendung von IFS= bleibt die Zeileneinrückung erhalten.

  2. Sie sollten fast immer die Option -r mit read verwenden.

  3. Lies keine Zeilen mit for

  4. Dateien ohne Zeilenumbruch sind keine Standard-Unix-Textdateien.

2
Jahid

Der folgende Code mit der umgeleiteten "while-read" -Schleife funktioniert für mich gut

while read LINE
do
      let count++
      echo "$count $LINE"

done < $FILENAME

echo -e "\nTotal $count Lines read"
0
Aniket Thakur