
Ich habe eine Datei wie diese:
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
Ich möchte eine Datei wie diese generieren:
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
Wie geht das mit Bash/Sed/Awk/Grep oder etwas Ähnlichem?
Antwort1
Wenn Sie ein Awk haben, das reguläre Ausdrücke für den Datensatztrenner unterstützt RS
, können Sie dies folgendermaßen machen:
awk 'BEGIN { RS = " +| *\\\\?\\n" } 1'
Der Vorteil hierbei besteht darin, dass wir nicht die gesamte Datei in den Speicher schnappen und einige reguläre Ausdrücke ersetzen. Ihre Eingabe könnte Gigabyte lang sein.
Grundsätzlich behandeln wir die Datei so, als hätte sie zwei Datensatztrennzeichen: entweder ein oder mehrere Leerzeichen oder null oder mehr Leerzeichen, gefolgt von einem Zeilenumbruch, dem optional ein Backslash vorangestellt sein kann.
Nachdem wir die Datensätze auf diese Weise abgegrenzt haben, müssen wir sie nur noch ausgeben, gefolgt vom Standard-Ausgabedatensatztrennzeichen ( ORS
), das natürlich Newline ist. Dies wird durch eine Muster-Aktionsregel erreicht, die aus besteht 1
.
Oder ein Pipeline-Job mit sed
und tr
, der nichts verwendet, was nicht in POSIX enthalten ist:
tr '\n' ' ' | sed -e 's/\\//g' -e 's/ \+/ /g' | tr ' ' '\n'
Ersetzen Sie Zeilenumbrüche durch Leerzeichen. Dann fassen Sie mehrere Leerzeichen zu einem Leerzeichen zusammen und entfernen Sie dabei Backslashs. Ordnen Sie dann Leerzeichen Zeilenumbrüchen zu.
Antwort2
Mit GNUgrep
$ cat file
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
$ grep -o '[^\ ]*' file
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
-o
nur übereinstimmende Muster extrahieren[^\ ]*
null oder mehr Zeichen, die kein Leerzeichen oder kein\
Buchstabe sind. Da*
es gierig ist, wird versucht, so viele solcher Buchstaben wie möglich zu finden
um das Ergebnis in einer anderen Datei zu speichern, verwenden Sie
$ grep -o '[^\ ]*' file > out_file
Wie @Stéphane Chazelas betont, ist es aus Gründen der Portabilität besser, Folgendes zu verwenden:
grep -oE '[^\ ]+' file
wobei -E
der erweiterte reguläre Ausdruck aufgerufen wird und [^\ ]+
ein oder mehrere Elemente abgeglichen werden, die kein Leerzeichen oder kein \
Zeichen sind.
Leistungsanalyse:
$ perl -ne 'print "$_"x100000' file > file_big
$ shuf file_big -o file_big
$ du -sh file_big
9.0M file_big
Alle Antworten und Anregungen aus den Kommentaren wurden zum Vergleich herangezogen:
$ time grep -o '[^\ ]*' file_big > o1
real 0m2.090s
user 0m2.076s
sys 0m0.016s
$ time grep -oE '[^\ ]+' file_big > o2
real 0m1.523s
user 0m1.504s
sys 0m0.012s
$ time awk 'BEGIN { RS = " +| *\\\\?\\n" } 1' file_big > o3
real 0m0.331s
user 0m0.320s
sys 0m0.008s
$ time tr -s '\\ ' '[\n*]' < file_big | grep . > o4
real 0m0.095s
user 0m0.124s
sys 0m0.008s
$ time tr '\\ ' '[\n*]' < file_big | grep . > o5
real 0m0.105s
user 0m0.104s
sys 0m0.016s
Gesundheitsüberprüfung
$ diff -s o1 o2
Files o1 and o2 are identical
$ diff -s o1 o3
Files o1 and o3 are identical
$ diff -s o1 o4
Files o1 and o4 are identical
$ diff -s o1 o5
Files o1 and o5 are identical