Scal dwa pliki linia po linii z symbolem potrójnej rurki separatora „|||”


14

Mam dwa równoległe pliki z taką samą liczbą wierszy w dwóch językach i planuję połączyć te dwa pliki linia po linii z separatorem |||. Np. Dwa pliki są następujące:

Plik 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.

Plik 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.

Oczekiwany wynik jest następujący:

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.

Próbowałem pastepolecenia, takiego jak:

paste -d "|||" fileA fileB

Ale zwracane dane wyjściowe zawierają tylko jedną potok, na przykład:

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

Czy jest jakiś sposób na oddzielenie każdej pary linii za pomocą trójnogu |||?


8
paste -d '|||' fileA - - fileB < /dev/null
Stéphane Chazelas

5
offtopic, ale twoje tłumaczenia są niepoprawne;) „Ich habe Durst” = Jestem ten, „Ich bin neu” = Jestem nowy ... niekoniecznie oznacza, że ​​jesteś głupi. ... na wypadek, gdybyś faktycznie uczył się niemieckiego ...
dave_alcarin

@ StéphaneChazelas Thx, ale moje dane wyjściowe nadal zawierają tylko jedną rurę ...
Frown

@dave_alcarin Dank sehr!
Zmarszczy

Odpowiedzi:


20

Z pastą POSIX :

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

pastepołączy odpowiednie linie wszystkich plików wejściowych. Tutaj mamy sześć plików, fileAcztery atrapy plików ze standardowych w -i fileB.

Lista ograniczników obejmuje spację, trzy potoki i spację w tej kolejności będą używane pastecyklicznie.

Dla pierwszego wiersza sześciu plików fileAzostanie połączony z pierwszym plikiem zastępczym (który jest niczym, dzięki operatorowi no-op :), produkuje line1-fileA<space>.

Pierwszy plik fikcyjny zostanie połączony z drugim przez potok, produkuj line1-fileA |, a następnie drugi plik fikcyjny z trzecim plikiem fikcyjnym, produkuj line1-fileA ||, trzeci plik fikcyjny z czwartym plikiem fikcyjnym, produkuj line1-fileA |||.

A czwarty plik atrapa z fileB, produkuj line1-fileA ||| line1-fileB.

Te kroki zostaną powtórzone dla wszystkich linii, dając oczekiwany wynik.


Użycie :|jest do pisania na maszynie mniej i głównie w interaktywnej powłoce. W skrypcie należy użyć:

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

aby zapobiec spawnowaniu podpowłoki.


1
+1 za :|. sprytna alternatywa dla</dev/null
cas

4
... i +1 za inteligentne użycie 4 fałszywych plików ze standardowego wejścia - - - -, ale następnym razem możesz nawet napisać kilka wierszy dla wyjaśnienia :)
Hastur

Dzięki, ale wciąż otrzymuję wynik za pomocą jednej rury ...
Zmarszczy

@hui, czy wykonałeś polecenie dokładnie tak, jak podano, uwzględniając wszystkie myślniki i spacje? Jaki jest twój system operacyjny?
Stéphane Chazelas

:|paste -d '|' fileA - - fileBdaje bardziej poprawną wersję bez separatora spacji.
Pål GD

7

Cóż, to nie używa sed, awk ani grep, ale możesz to zrobić dość łatwo w bash. Polecenie to:

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

Problem z wklejaniem polega na tym, że separator jest pojedynczym znakiem. Możesz także wstawić pojedynczy znak i użyć sed, aby go przekształcić, ale byłoby to podatne na błędy, jeśli znak już pojawił się w pliku wejściowym.


2
Twoje rozwiązanie nie będzie działać, jeśli wiersz zawiera znak odwrotnego ukośnika lub zacznie się od myślnika. Chcesz użyć IFS=przed każdym read. Możesz łatwo to zrobić paste. Zobacz moją odpowiedź , a także tę, aby dowiedzieć się, dlaczego warto unikać używania whilepętli w skrypcie powłoki.
cuonglm,

Działa dla mojego pliku. Wielkie dzięki !!!
Zmarszczy

5

Wersja awk (GNU)

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

Za pomocą getlinepolecenia w awkmożesz ustawić $0(wszystkie zmienne dla kolumn) z następnego rekordu wejściowego, jeśli getline < "filename"ustawisz następny $0z określonego pliku.

getline <"plik" Ustaw 0 $ od następnego rekordu pliku; ustaw NF.


Dlaczego Twoja próba nie zadziałała zgodnie z oczekiwaniami? Z man pastemożemy przeczytać

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

ale używa ograniczników po jednym dla każdej kolumny .

Więc polecenie
paste -d '|*|*' fileA fileB fileA fileBpodaje mi linie jako

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...


sedRozwiązanie, które proponuję, aby uniknąć nawet jeśli blisko do oryginalnej próbie, gdyż łata uzyskany zachowania do swojego pierwotnego celu:

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

Aby tego uniknąć, ponieważ zastępujesz każdy wzorzec |nowym |||, ale musisz założyć, że symbol potoku ( |) nie jest obecny w twoich danych , w przeciwnym razie musisz poradzić sobie ze specjalnymi przypadkami i uczynić kod bardziej złożonym, aby uniknąć skutków ubocznych.


Wariant z konstrukcją Here String [ 1 ]<<<

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

Ustawiasz 5 ograniczników za pomocą -d ' ||| '(spacja, |, |, |, spacja) i 4 plików zastępczych ( - - - -), które będą pobierać dane z pustego ciągu ''.


Testowane na GNU Awk 4.0.1, wklej (GNU coreutils) 8.21 i sed (GNU sed) 4.2.2


Dzięki, polecenie awk działa!
Zmarszczy

1
Proszę bardzo. Zaktualizowano odpowiedź dodając sedprzykład, aby uniknąć (:-)) i więcej komentarzy.
Hastur

4

Jeśli chcesz uniknąć magii i dramaturgii okrągłych separatorów i plików zastępczych, możesz po prostu dołączyć separator do jednego pliku przed ich wklejeniem:

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

daje

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.

Lubię to dla uproszczenia. Uważam, że masz na myśli „prepend”, a nie „append”. Zapoznaj się z odpowiedzią Hastura na awk dla tej wersji awk.
Wildcard

Powinieneś zmienić podstawienie procesu na potok, więc nie będziesz mieć limitu liczby obsługiwanych przez niego powłok.
cuonglm

@Wildcard tak, dodawaj, ale przepiszę, aby dołączyć do filea. Myślę, że awk to trochę przesada.
snth

@cuonglm to prawda, ale chciałem uniknąć rur dla przejrzystości. Czułem, że fajka sprawi, że zacznie wyglądać jak atrapy plików, ale masz rację
snth

0

możesz to zrobić w Pythonie również w ten sposób.

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.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.