Verwenden Sie Bash, um Zeile für Zeile zu lesen und Platz zu sparen

74

Wenn ich "cat test.file" verwende, wird es angezeigt

1
 2
  3
   4

Wenn ich die Bash-Datei verwende,

cat test.file |
while read data
do
    echo "$data"
done

Wird sich zeigen

1
2
3
4

Wie kann ich das Ergebnis genau wie die ursprüngliche Testdatei erstellen?

blasser
quelle
Man kann nützliche Skriptzeilen finden, um stdio Zeilennummern voranzustellen: { i=0; while read; do i=$(dc 1 $i + p); printf "%4 d $REPLY\n" $i; done; }Es ist kompatibel mit alten SH.
Kyb

Antworten:

116
IFS=''
cat test.file |
while read data
do
    echo "$data"
done

Mir ist klar, dass Sie das Beispiel vielleicht von etwas vereinfacht haben, das wirklich eine Pipeline brauchte, aber bevor es jemand anderes sagt:

IFS=''
while read data; do
    echo "$data"
done < test.file
DigitalRoss
quelle
1
Da das Skript in eine einzelne Variable pro Zeile einliest, bleiben alle Leerzeichen in den Daten (nach dem ersten nicht leeren) unabhängig davon erhalten. Das leere IFS behält jedoch die führenden Leerzeichen (in ksh und bash) bei.
Jonathan Leffler
27
Dies ist ein Ort, an dem Sie schreiben könntenwhile IFS= read data; ...
Glenn Jackman
@glennjackman Ich fing an zu versuchen export IFS..., gut, ich habe dir einen Tipp vorgelesen!
Wassermann Power
10
Möglicherweise möchten Sie verwenden IFS= read -r line, um die Interpretation von Backslashes zu vermeiden.
Lekensteyn
28

Wenn Sie dem Aufruf "read" kein Argument angeben, setzt read eine Standardvariable namens $ REPLY, die Leerzeichen beibehält. Sie können dies also einfach tun:

$ cat test.file | while read; do echo "$REPLY"; done
Joshua Davies
quelle
1
Diese Antwort veranlasste mich, die Dokumentation für genau zu lesen read.
David Cullen
3
Beachten Sie, dass Zeichen mit Backslash-Escapezeichen wie \nin Ihrer Quelldatei (dh Quellcode) readin just konvertiert werden n. Verwenden Sie read -r, um dies zu verhindern.
Jdgregson
1
Beachten Sie, dass diese $REPLYLösung in ZSH nicht funktioniert, die IFS= readLösung jedoch sowohl in ZSH als auch in Bash.
Ntc2
4

Nur um die Antwort von DigitalRoss zu ergänzen.

Für den Fall, dass Sie das IFS nur für diesen Befehl ändern möchten , können Sie geschweifte Klammern verwenden. Wenn Sie dies tun, wird der Wert von IFS nur innerhalb des Blocks geändert. So was:

echo '
  word1
  word2' |  { IFS='' ; while read line ; do echo "$line" check ; done ; }

Die Ausgabe lautet (Leerzeichen behalten):

  word1 check
  word2 check
diogovk
quelle
Auch wenn das IFSUnset dieses spezielle Beispiel bereits rettet, sollten Sie dennoch zitieren "$line". In einem Beispiel aus der realen Welt könnte der Wert immer noch Shell-Platzhalter enthalten und was nicht.
Tripleee
1
Falsch , so herabgestimmt! Verwenden Sie IFS='' read -r linestattdessen! In Ihrem Beispiel läuft die Pipe {..}in einer Subshell! Wie IFS=' '; ( IFS='' ); echo "=$IFS="welche Drucke = =(beachten Sie die SPC). Im Gegensatz dazu IFS=' '; { IFS=''; }; echo "=$IFS="druckt ==(IFS von innen {..}). Ich würde jedoch eher empfehlen: fullread() { local IFS=''; read -r $1; }oder IFS='' read -r line(es gibt kein ;' between IFS = '' `und read. In Shells VAR=val cmd args..ändern Sie die Umgebung für nur einen Befehl (dies funktioniert auch für integrierte Funktionen wie read).
Tino
1
Ich bin mir nicht sicher, ob ich folge, Tino. Versuchen Sie es cat -etv <<<"$IFS" ; echo word1 | { IFS=''; cat -etv <<<"$IFS" ; } ; cat -etv <<<"$IFS" ;. Die Subshell hat das IFS außerhalb des Blocks nicht wie erwartet geändert. Können Sie das näher erläutern?
Diogovk
2

Vielleicht IFSist der entscheidende Punkt, wie andere sagten. Sie müssen nur IFS=zwischen whileund hinzufügen read.

cat test.file | 
while IFS= read data 
 do echo "$data"
 done

und vergessen Sie nicht Zitate von $data, sonst echowerden die Leerzeichen gekürzt.

Aber wie Joshua Davies erwähnt hat , würden Sie es vorziehen, die vordefinierte Variable zu verwenden $REPLY.

plhn
quelle
0

read datateilt die Daten nach IFS auf , was normalerweise "\ t \ n" ist. Dadurch bleiben die Lücken für Sie erhalten:

var=$(cat test.file)
echo "$var"
Mu Qiao
quelle
0

Verwenden Sie alternativ ein gutes Tool zum Parsen von Dateien wie AWK :

awk '{
  # Do your stuff
  print 
}' file
Ghostdog74
quelle