abstrakt
Zeilen ohne Zeilenumbruch drucken, nur dann eine Zeilenumbruch hinzufügen, wenn eine andere Zeile gedruckt werden soll.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Andere Lösungen
Wenn wir mit einer Datei gearbeitet haben, können wir nur ein Zeichen davon abschneiden (wenn es in einer neuen Zeile endet):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || -s-1 "$ 1" abschneiden; }}
Dies ist eine schnelle Lösung, da nur ein Zeichen aus der Datei gelesen und dann direkt ( truncate
) entfernt werden muss, ohne die gesamte Datei zu lesen.
Während der Arbeit mit Daten aus stdin (einem Stream) müssen jedoch alle Daten gelesen werden. Und es wird "verbraucht", sobald es gelesen wird. Kein Backtrack (wie beim Abschneiden). Um das Ende eines Streams zu finden, müssen wir bis zum Ende des Streams lesen. Zu diesem Zeitpunkt gibt es keine Möglichkeit, zum Eingabestream zurückzukehren. Die Daten wurden bereits "verbraucht". Dies bedeutet, dass Daten in einer Art Puffer gespeichert werden müssen , bis wir mit dem Ende des Streams übereinstimmen und dann etwas mit den Daten im Puffer tun.
Die naheliegendste Lösung besteht darin, den Stream in eine Datei zu konvertieren und diese Datei zu verarbeiten. Aber die Frage fragt nach einer Art Filter des Streams. Nicht über die Verwendung zusätzlicher Dateien.
Variable
Die naive Lösung wäre, die gesamte Eingabe in eine Variable zu erfassen:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
Erinnerung
Es ist möglich, mit sed eine ganze Datei in den Speicher zu laden. In sed ist es unmöglich, die nachfolgende neue Zeile in der letzten Zeile zu vermeiden. GNU sed vermeidet möglicherweise das Drucken einer nachfolgenden Zeilenumbruch, jedoch nur, wenn die Quelldatei bereits fehlt. Also, nein, einfaches Sed kann nicht helfen.
Außer auf GNU awk mit der -z
Option:
sed -z 's/\(.*\)\n$/\1/'
printf
Schlürfen Sie mit awk (any awk) den gesamten Stream und dies ohne die nachfolgende Newline.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Das Laden einer ganzen Datei in den Speicher ist möglicherweise keine gute Idee, da sie möglicherweise viel Speicher verbraucht.
Zwei Zeilen im Speicher
In awk können wir zwei Zeilen pro Schleife verarbeiten, indem wir die vorherige Zeile in einer Variablen speichern und die aktuelle drucken:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Direkte Bearbeitung
Aber wir könnten es besser machen.
Wenn wir die aktuelle Zeile ohne neue Zeile drucken und eine neue Zeile nur dann drucken, wenn eine nächste Zeile vorhanden ist, verarbeiten wir jeweils eine Zeile und die letzte Zeile enthält keine nachfolgende neue Zeile:
awk 'NR == 1 {printf ("% s", $ 0); next}; {printf ("\ n% s", $ 0)} '
Oder anders geschrieben:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Oder:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
So:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, dachomp
höchstens eine nachfolgende Newline gelöscht wird.