Füge zwei Dateien Zeile für Zeile mit dem Begrenzer-Dreifach-Pipe-Symbol "|||" zusammen

14

Ich habe zwei parallele Dateien mit der gleichen Anzahl von Zeilen in zwei Sprachen und plane, diese beiden Dateien zeilenweise mit dem Trennzeichen zusammenzuführen |||. ZB sind die zwei Dateien wie folgt:

Datei A:

1Mo 1,1 I love you.
1Mo 1,2 I like you.
Hi 1,3 I am hungry.
Hi 1,4 I am foolish.

Datei B:

1Mo 1,1 Ich liebe dich.
1Mo 1,2 Ich mag dich.
Hi 1,3 Ich habe Durst.
Hi 1,4 Ich bin neu.

Die erwartete Ausgabe sieht folgendermaßen aus:

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.

Ich habe den pasteBefehl wie folgt ausprobiert :

paste -d "|||" fileA fileB

Die zurückgegebene Ausgabe enthält jedoch nur eine Pipe, z.

1Mo 1,1 I love you. |1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. |1Mo 1,2 Ich mag dich.

Gibt es eine Möglichkeit, jedes Leitungspaar durch ein Tripe Pipe zu trennen |||?

Stirnrunzeln
quelle
8
paste -d '|||' fileA - - fileB < /dev/null
Stéphane Chazelas
5
offtopic, aber Ihre Übersetzungen sind nicht korrekt;) "Ich habe Durst" = Ich bin thisrty, "Ich bin neu" = Ich bin neu ... bedeutet nicht unbedingt, dass Sie dumm sind. ... nur für den Fall, dass Sie tatsächlich Deutsch lernen ...
dave_alcarin
@ StéphaneChazelas Thx, aber meine Ausgabe enthält immer noch nur eine Pipe ...
Stirnrunzeln
@dave_alcarin Dank sehr!
Stirnrunzeln

Antworten:

20

Mit POSIX einfügen :

:|paste -d ' ||| ' fileA - - - - fileB

pasteverkettet die entsprechenden Zeilen aller Eingabedateien. Hier haben wir sechs Dateien, fileAvier Dummy-Dateien von Standard in -, und fileB.

Die Liste der Begrenzer enthält ein Leerzeichen, drei Pipe und ein Leerzeichen in dieser Reihenfolge, das pastezirkulär verwendet wird .

Für die erste Zeile werden sechs Dateien, fileAdie mit der ersten Dummy-Datei verkettet werden (die dank des no-op: -Operators nichts ist ), erzeugt line1-fileA<space>.

Die erste Dummy-Datei wird durch eine Pipe mit der zweiten verkettet line1-fileA |, dann die zweite Dummy-Datei mit der dritten Dummy-Datei erzeugen line1-fileA ||, die dritte Dummy-Datei mit der vierten Dummy-Datei erzeugen line1-fileA |||.

Und die vierte Dummy-Datei mit fileB, produzieren line1-fileA ||| line1-fileB.

Dieser Schritt wird für alle Zeilen wiederholt, um das erwartete Ergebnis zu erhalten.


Die Verwendung von :|ist für weniger tippen und vor allem in der interaktiven Shell. In einem Skript sollten Sie Folgendes verwenden:

</dev/null paste -d ' ||| ' fileA - - - - fileB

um zu verhindern, dass eine Subshell erzeugt wird.

cuonglm
quelle
1
+1 für die :|. clevere Alternative zu</dev/null
cas
4
... und +1 für die kluge Verwendung von 4 Dummy-Dateien aus der Standardeingabe mit - - - -, aber beim nächsten Mal können Sie sogar ein paar Zeilen zur Erklärung schreiben :)
Hastur
Danke, aber ich bekomme immer noch die Ausgabe mit einer Pipe ...
Frown
@hui, hast du den Befehl genau wie angegeben ausgeführt, einschließlich aller Bindestriche und Leerzeichen? Was ist Ihr Betriebssystem?
Stéphane Chazelas
:|paste -d '|' fileA - - fileBgibt die korrektere Version ohne Leerzeichen an.
Pål GD
7

Nun, das benutzt weder sed, awk noch grep, aber du kannst es ziemlich einfach in bash machen. Der Befehl lautet:

(while IFS= read -r a <&3 && IFS= read -r b <&4; do echo "$a ||| $b"; done) 3<fileA 4<fileB

Das Problem beim Einfügen ist, dass das Trennzeichen ein einzelnes Zeichen ist. Sie können auch ein einzelnes Zeichen einfügen und es mit sed transformieren. Dies ist jedoch fehleranfällig, wenn das Zeichen bereits in der Eingabedatei enthalten ist.

user3188445
quelle
2
Ihre Lösung funktioniert nicht, wenn die Zeile einen umgekehrten Schrägstrich enthält oder mit einem Bindestrich beginnt. Sie möchten IFS=vor jedem verwenden read. Sie können es leicht mit tun paste. Siehe meine Antwort und auch diese, um zu sehen, warum die Verwendung von whileSchleifen in Shell-Skripten vermieden werden sollte .
Donnerstag,
Es funktioniert für meine Datei. Vielen Dank !!!
Stirnrunzeln
5

Eine awk (GNU) Version

awk '{printf ("%s ||| ", $0); getline < "fileB"; print $0 }' fileA

Mit dem getlineBefehl in awkkönnen Sie $0(alle Variablen für Spalten) ab dem nächsten Eingabedatensatz getline < "filename"festlegen , wenn Sie den nächsten $0aus der angegebenen Datei festlegen .

getline <"file" Setzt $ 0 aus dem nächsten Datensatz der Datei; set NF.


Warum hat Ihr Versuch nicht wie erwartet funktioniert? Daraus können man pastewir lesen

-d, --delimiters=LIST
     reuse characters from LIST instead of TABs

aber es nutzt die Begrenzungszeichen eines für jede Spalte .

Also
paste -d '|*|*' fileA fileB fileA fileBgibt der Befehl mir Zeilen als

Hi 1,3 I am hungry.|Hi 1,3 Ich habe Durst.*Hi 1,3 I am hungry.|Hi 1,3 Ich...
Hi 1,4 I am foolish.|Hi 1,4 Ich bin neu.*Hi 1,4 I am foolish.|Hi 1,4 Ich...


Eine sedLösung, die ich selbst dann vermeiden möchte, wenn sie Ihrem ursprünglichen Versuch nahe kommt, da sie das erhaltene Verhalten an Ihren ursprünglichen Zweck anpasst:

 paste -d '|' fileA fileB | sed 's/|/|||/g'

Um zu vermeiden, dass Sie jedes Muster |durch das neue ersetzen |||, aber davon ausgehen müssen, dass das Pipe-Symbol ( |) in Ihren Daten nicht vorhanden ist , müssen Sie sich mit Sonderfällen befassen und den Code komplexer gestalten, um Nebenwirkungen zu vermeiden.


Eine Variante mit dem Here String [ 1 ] -Konstrukt<<<

 paste -d ' ||| ' fileA - - - - fileB  <<< ''

Sie setzen 5 Trennzeichen mit -d ' ||| '(Leerzeichen, |, |, |, Leerzeichen) und 4 Dummy-Dateien ( - - - -), die Daten aus der leeren Zeichenfolge übernehmen ''.


Getestet auf GNU Awk 4.0.1, Paste (GNU Coreutils) 8.21 und Sed (GNU Sed) 4.2.2

Hastur
quelle
Danke, der awk Befehl funktioniert!
Stirnrunzeln
1
Gern geschehen. Aktualisierte die Antwort und fügte ein sedBeispiel hinzu, um (:-)) und mehr Kommentare zu vermeiden.
Hastur
4

Wenn Sie die Magie und das Drama kreisförmiger Trennzeichen und Dummy-Dateien vermeiden möchten, können Sie Ihr Trennzeichen einfach an eine Datei anhängen, bevor Sie sie einfügen:

paste <(sed 's/$/ |||/' filea) fileb

gibt

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. |||    Hi 1,4 Ich bin neu.
snth
quelle
Ich mag das der Einfachheit halber. Ich glaube, Sie meinen "voranstellen", aber nicht "anhängen". Kasse Hastur awk Antwort für die awk-Version davon.
Wildcard
Sie sollten die Prozessersetzung in eine Pipe ändern, damit die Anzahl der unterstützten Shells nicht überschritten wird.
Dienstag,
@Wildcard ja, voranstellen, aber ich werde es umschreiben, um an filea anzuhängen. Ich denke, awk ist ein bisschen übertrieben.
28.
@cuonglm stimmt, aber ich wollte aus Gründen der Übersichtlichkeit Pfeifen vermeiden. Ich hatte das Gefühl, eine Pipe würde es so aussehen lassen wie die Dummy-Dateien, aber Sie haben
Recht
0

Sie können es auch in Python auf diese Weise tun.

lines1 = [ line.rstrip() for line in open("file1") ]
lines2 = [ line.rstrip() for line in open("file2") ]
for i in xrange((len(lines1))): print lines1[i] + " ||| " + lines2[i]
... 
1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.
c4f4t0r
quelle