So verschachteln Sie Zeilen aus zwei Textdateien

77

Was ist der einfachste / schnellste Weg, um die Zeilen von zwei (oder mehr) Textdateien zu verschachteln? Beispiel:

Datei 1:

line1.1
line1.2
line1.3

Datei 2:

line2.1
line2.2
line2.3

Interleaved:

line1.1
line2.1
line1.2
line2.2
line1.3
line2.3

Sicher ist es einfach, ein kleines Perl-Skript zu schreiben, das beide öffnet und die Aufgabe erledigt. Aber ich habe mich gefragt, ob es möglich ist, mit weniger Code davonzukommen, vielleicht einem Einzeiler mit Unix-Tools?

Frank
quelle

Antworten:

147
paste -d '\n' file1 file2
Codaddict
quelle
3
Hinweis: Auf einigen Plattformen pasteist die Anzahl der Eingabedateien begrenzt. Unter Solaris können maximal 12 Eingabedateien verwendet werden, und die Ausgabezeilen sind auf 511 Zeichen begrenzt.
user667489
Und dann verwenden Sie dies zusammen mit der Prozessersetzung in einem Beispiel wie paste -d '\n' <(find /) <(find /)und erkennen Sie, wie viel besser dies ist, als zuerst die Ergebnisse in jeder Hinsicht in eine Datei zu schreiben, Festplattennutzung, Speichernutzung, Parallelisierung, Abbrechbarkeit ...
masterxilo
1
Wow paste! Zurück zu 1979 mit AT & T UNIX 32v . Gut gemacht, Sir.
emallove
6

Hier ist eine Lösung mit awk:

awk '{print; if(getline < "file2") print}' file1

erzeugt diese Ausgabe:

line 1 from file1
line 1 from file2
line 2 from file1
line 2 from file2
...etc

Die Verwendung awk kann nützlich sein, wenn Sie der Ausgabe zusätzliche Formatierungen hinzufügen möchten, z. B. wenn Sie jede Zeile anhand der Datei beschriften möchten, aus der sie stammt:

awk '{print "1: "$0; if(getline < "file2") print "2: "$0}' file1

erzeugt diese Ausgabe:

1: line 1 from file1
2: line 1 from file2
1: line 2 from file1
2: line 2 from file2
...etc

Hinweis: In diesem Code wird davon ausgegangen, dass Datei1 größer oder gleich lang ist wie Datei2.

Wenn Datei1 mehr Zeilen als Datei2 enthält und Sie nach Abschluss leere Zeilen für Datei2 ausgeben möchten, fügen Sie dem getline-Test eine else-Klausel hinzu:

awk '{print; if(getline < "file2") print; else print ""}' file1

oder

awk '{print "1: "$0; if(getline < "file2") print "2: "$0; else print"2: "}' file1
Samgak
quelle
3

Die Antwort von @ Sujoy weist in eine nützliche Richtung. Sie können Zeilennummern hinzufügen, sortieren und die Zeilennummern entfernen:

(cat -n file1 ; cat -n file2 )  | sort -n  | cut -f2-

Beachten Sie (für mich von Interesse), dass dies etwas mehr Arbeit erfordert, um die richtige Reihenfolge zu erreichen, wenn Sie anstelle von statischen Dateien die Ausgabe von Befehlen verwenden, die möglicherweise langsamer oder schneller als die anderen ausgeführt werden. In diesem Fall müssen Sie zusätzlich zu den Zeilennummern ein weiteres Tag hinzufügen / sortieren / entfernen:

(cat -n <(command1...) | sed 's/^/1\t/' ; cat -n <(command2...) | sed 's/^/2\t/' ; cat -n <(command3) | sed 's/^/3\t/' )  \
   | sort -n  | cut -f2- | sort -n | cut -f2-
Joshua Goldberg
quelle
1

Hier ist eine GUI-Methode: Fügen Sie sie in zwei Spalten in einer Tabelle ein, kopieren Sie alle Zellen heraus und ersetzen Sie Tabulatoren durch reguläre Ausdrücke durch Zeilenumbrüche.

Dwedit
quelle
-1
cat file1 file2 |sort -t. -k 2.1

Hier wird angegeben, dass der Separator "." Ist. und dass wir nach dem ersten Zeichen des zweiten Feldes sortieren.

Sujoy
quelle
Ich verstehe nicht, wie das funktioniert. Zum Beispiel gibt es keinen Doppelpunkt in der Sortiereingabe? Kannst du mehr erklären?
Frank
Mein schlechtes, änderte das ':' in '.' Es ist nur das Trennzeichen. Wenn keine Sortierung angegeben ist, werden Felder durch Leerzeichen getrennt. man sortFür mehr Information.
Sujoy
7
Diese Antwort setzt voraus, dass die Eingabe tatsächlich die in der Frage beschriebene wörtliche Form hat. Ich denke, das sollte illustrativ sein. Es wäre möglich, jede Eingabedatei auf diese Weise zu transformieren, aber es würde viel mehr Durchgänge durch die Daten geben. Die Antwort von codaddict ist besser.
Phil Miller
@ Novelocrat: Ja, das ist nur ein anderer Weg, es zu tun :)
Sujoy