Ich habe Dateien, die in einer oder mehreren Zeilenumbrüchen enden und nur in einer Zeile enden sollten. Wie kann ich das mit Bash / Unix / GNU-Tools machen?
Beispiel für eine fehlerhafte Datei:
1\n
\n
2\n
\n
\n
3\n
\n
\n
\n
Beispiel korrigierte Datei:
1\n
\n
2\n
\n
\n
3\n
Mit anderen Worten: Zwischen dem EOF und dem letzten Nicht-Newline-Zeichen der Datei sollte genau eine Newline stehen.
Referenzimplementierung
Dateiinhalt lesen, eine neue Zeile abschneiden, bis keine neuen Zeilen mehr vorhanden sind, zurückschreiben:
#! /bin/python
import sys
with open(sys.argv[1]) as infile:
lines = infile.read()
while lines.endswith("\n\n"):
lines = lines[:-1]
with open(sys.argv[2], 'w') as outfile:
for line in lines:
outfile.write(line)
Klarstellung: Natürlich sind Rohrleitungen erlaubt, wenn diese eleganter sind.
sed
Vorschlag sah, dachte ich nur OMG ...awk: illegal statement
.brew install mawk
und ändern Sie den Befehl aufmawk
funktioniert.Aus nützlichen einzeiligen Skripten für sed .
quelle
find . -type f -name '*.js' -exec sed --in-place -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
find . -type f -name '*.js' -exec sed -i '' -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
Da hast du schon Antworten mit den passenderen Werkzeugen sed und awk; Sie können die Tatsache ausnutzen, dass
$(< file)
nachgestellte Leerzeilen entfernt werden.Dieser billige Hack würde nicht funktionieren, um nachgestellte Leerzeilen zu entfernen, die Leerzeichen oder andere nicht druckbare Zeichen enthalten könnten, sondern nur, um nachgestellte Leerzeilen zu entfernen. Es funktioniert auch nicht, wenn die Datei null Bytes enthält.
Verwenden Sie in anderen Shells als bash und zsh
$(cat file)
anstelle von$(<file)
.quelle
$()
verwirft nachfolgende Zeilenumbrüche. Das ist eine Designentscheidung. Ichecho "On $(date ...) we will meet."
gehe davon aus, dass dies die Integration in andere Strings erleichtern wird: wäre böse mit der Newline, die fast jeder Shell-Befehl am Ende ausgibt.[[ $a == '' ]] || printf '%s\n' "$a" >"$file"
.a=$(gtac file.txt); printf '%s\n' "$a" | gtac > file.txt
Sie können diesen Trick mit
cat
& verwendenprintf
:Beispielsweise
Das
$
kennzeichnet das Ende einer Zeile.Verweise
quelle
Diese Frage ist mit ed markiert , aber niemand hat eine
ed
Lösung vorgeschlagen .Hier ist eine:
oder äquivalent,
ed
platziert Sie beim Start standardmäßig in der letzten Zeile des Bearbeitungspuffers.Der erste Befehl (
a
) fügt eine leere Zeile an das Ende des Puffers an (die leere Zeile im Bearbeitungsskript ist diese Zeile, und der Punkt (.
) dient nur zum Zurückkehren in den Befehlsmodus).Der zweite Befehl (
?
) sucht nach der nächsten vorherigen Zeile, die etwas enthält (sogar Leerzeichen), und löscht dann ab der nächsten Zeile alles bis zum Ende des Puffers.Der dritte Befehl (
w
) schreibt die Datei zurück auf die Festplatte.Die hinzugefügte Leerzeile schützt den Rest der Datei vor dem Löschen, falls am Ende der Originaldatei keine Leerzeilen vorhanden sind.
quelle
Hier ist eine Perl-Lösung, bei der nicht mehr als eine Zeile gleichzeitig in den Speicher eingelesen werden muss:
oder als Einzeiler:
Dadurch wird die Datei zeilenweise gelesen und jede Zeile daraufhin überprüft, ob sie ein Nicht-Zeilenumbruchzeichen enthält. Wenn dies nicht der Fall ist, wird ein Zähler inkrementiert. In diesem Fall wird die Anzahl der vom Zähler angegebenen Zeilenumbrüche gefolgt von der Zeile selbst gedruckt und der Zähler anschließend zurückgesetzt.
Selbst das Puffern einer einzelnen Zeile im Speicher ist technisch nicht erforderlich. Es wäre möglich, dieses Problem unter Verwendung einer konstanten Speichermenge zu lösen, indem die Datei in Blöcken fester Länge gelesen und zeichenweise unter Verwendung einer Zustandsmaschine verarbeitet wird. Ich vermute jedoch, dass dies für den typischen Anwendungsfall unnötig kompliziert wäre.
quelle
Wenn Ihre Datei klein genug ist, um in den Speicher zu gelangen, können Sie dies verwenden
quelle
In Python (ich weiß, es ist nicht das, was Sie wollen, aber es ist viel besser, da es optimiert ist und ein Vorspiel für die Bash-Version), ohne die Datei neu zu schreiben und ohne die gesamte Datei zu lesen (was eine gute Sache ist, wenn die Datei ist sehr groß):
Beachten Sie, dass es nicht für Dateien funktioniert, bei denen das EOL-Zeichen nicht '\ n' ist.
quelle
Eine Bash-Version, die den Python-Algorithmus implementiert, aber weniger effizient ist, da sie viele Prozesse benötigt:
quelle
Dieser ist schnell zu tippen und, wenn Sie sed kennen, leicht zu merken:
Mit dem sed-Skript werden führende Leerzeilen aus nützlichen einzeiligen Skripten für sed , auf die Alexey oben verweist, und tac (reverse cat) gelöscht .
In einem Schnelltest mit einer Datei mit 18 MB und 64.000 Zeilen war Alexeys Ansatz schneller (0,036 gegenüber 0,046 Sekunden).
quelle