while read-Schleife stoppt nach der ersten Zeile, obwohl sie nicht von stdin liest

while read-Schleife stoppt nach der ersten Zeile, obwohl sie nicht von stdin liest

Ich habe eine Datei namens „ testenthält

test
test

und ich führe diesen Befehl aus

while read line
do
echo "$line"
done </tmp/test

Dies sollte „test“ zwei Mal ausgeben, gibt es aber nur einmal aus. Die Verwendung while IFS= read -r lineändert nichts. Das Problem lässt sich beheben, indem eine dritte leere Zeile ausgegeben wird, aber es sollte trotzdem funktionieren.

Antwort1

Versuchen:

while IFS='' read -r line || [ "$line" ]

Ja, es liest von der Standardeingabe (stdin), die <testUmleitung erfolgt zu stdin.

Das Problem könnte darin liegen, dass Read für die Arbeit mit Textdateien konzipiert ist.Korrekte Textdateien müssen mit einer neuen Zeile enden.

Ein ... machenDateiende bei einem Zeilenumbruch erweist sich als sehr schnell:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

Wenn Sie die Datei jedoch nicht bearbeiten können, um eine neue Zeile hinzuzufügen (falls diese fehlt), besteht die Lösung darin, (zusätzlich) zu testen, ob etwas gelesen wurde:

while read line || [ "$line" ]

Dadurch wird die Schleife ausgeführt, wenn das Lesen fehlgeschlagen ist (das Ende der Datei erreicht wurde, ohne eine neue Zeile zu lesen (standardmäßig)), aber etwas eingelesen wurde (in "$line").

Dies funktioniert ordnungsgemäß für Dateien, die mit einer neuen Zeile enden, unabhängig davon, ob sie mit einer neuen Zeile enden oder nicht.

Nein, ein IFS='' readhat keinen (direkten) Einfluss auf das Lesen der letzten Zeile, tatsächlich ist das IFS=''(im Vergleich zu einem Standard-IFS[A]von Leerzeichen, Tabulatoren, Zeilenumbrüchen) wirkt sich nur auf die Aufteilung inmehrereVariablen (neben der Entfernung von führenden und/oder nachfolgenden Leerzeichen (wenn IFS nur Leerzeichen enthält[B])). Da es nur eine Variable () gibt, "$line"muss keine Aufteilung durchgeführt werden.

Auch die -dOption zum Lesen (in Bash seit 2.04) hilft nicht weiter. Es gibt kein spezifisches „Dateiende“-Zeichen, das übereinstimmen müsste (das letzte Byte könnte ein beliebiges sein).

Die einzigen verbleibenden Optionen sind entweder:

  • um die Datei zu reparieren und sie in eine korrekte Textdatei umzuwandeln
  • Testen Sie, ob etwas in die Variable eingelesen wurde line.

Das richtige Skript wäre dann:

#!/bin/bash
while IFS='' read -r line || [ "$line" ]
do
    printf '%s\n' "$line"
done <test

IFS='' wird verwendet, um Probleme mit ungewöhnlichen IFS-Werten zu vermeiden.


[ein][b]Wenn IFS einen beliebigen Wert haben kann, werden natürlich viele andere Zeichen entfernt. Versuchen Sie es (für Werte, die mit „x“ enden)

printf "test\ntesx\ntest" | while IFS="x" read -r line || [ "$line" ]; do echo "$line"; done

Nein, zsh macht etwas anderes.

verwandte Informationen