Połącz dwa pliki z awk

9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Poszukiwany wynik:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Jak mogę to zrobić?

pawana
źródło

Odpowiedzi:

11

Poniższa odpowiedź oparta jest na podobnych pytaniach i odpowiedziach w SO z pewnymi istotnymi modyfikacjami:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($2 in dict) ? dict[$2] : $2}1' file2.txt file1.txt 
item1 platA
item2 platB
item3 platC
item4 platD
item5 platE

Pomysł polega na utworzeniu mapy skrótów z indeksem i użyciu jej jako słownika.

W przypadku drugiego pytania zadanego w komentarzu ( co należy zmienić, jeśli druga kolumna file1.txtbędzie szóstą kolumną ):

Jeśli plik wejściowy będzie podobny do file1b.txt:

item1 A5 B C D carA
item2 A4 1 2 3 carB
item3 A3 2 3 4 carC
item4 A2 4 5 6 platD
item5 A1 7 8 9 carE

Zrobi to następujące polecenie:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($6 in dict) ? dict[$6] : $6;$3="";$4="";$5="";$6=""}1' file2.txt file1b.txt 
item1 platA    
item2 platB    
item3 platC    
item4 platD    
item5 platE    
Yaron
źródło
1
@pawana - Zaktualizowałem swoją odpowiedź, aby rozwiązać również drugie pytanie w komentarzu. Jeśli odpowiedziałem na twoje pytanie, zaakceptuj je.
Yaron,
6

Wiem, że powiedziałeś awk, ale joinw tym celu istnieje polecenie ...

{
  join -o 1.1,2.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt)     
  join -v 1 -o 1.1,1.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt) 
} | sort -k 1

Wystarczyłoby pierwsze joinpolecenie, gdyby nie ten wiersz:

item4   platD

Komenda w zasadzie mówi: połącz na podstawie drugiej kolumny pierwszego pliku ( -1 2) i pierwszej kolumny drugiego pliku ( -2 1), i wypisz pierwszą kolumnę pierwszego pliku i drugą kolumnę drugiego pliku ( -o 1.1,2.2). To pokazuje tylko sparowane linie. Drugie polecenie łączenia mówi prawie to samo, ale mówi, aby pokazać wiersze z pierwszego pliku, którego nie można sparować ( -v 1), i wypisać pierwszą kolumnę pierwszego pliku i drugą kolumnę pierwszego pliku ( -o 1.1,1.2). Następnie sortujemy dane wyjściowe obu połączonych. sort -k 1oznacza sortowanie na podstawie pierwszej kolumny i sort -k 2sortowanie na podstawie drugiej. Ważne jest sortowanie plików na podstawie kolumny łączenia przed przekazaniem ich do join.

Teraz napisałem sortowanie dwa razy, ponieważ nie lubię zaśmiecać moich katalogów plikami, jeśli mogę pomóc. Jednak, jak powiedział David Foerster, w zależności od rozmiaru plików, możesz posortować pliki i zapisać je najpierw, aby nie musieć czekać na sortowanie dwa razy. Aby dać wyobrażenie o rozmiarach, oto czas potrzebny na posortowanie 1 miliona i 10 milionów linii na moim komputerze:

$ ruby -e '(1..1000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 1million.txt 
$ ruby -e '(1..10000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 10million.txt 
$ head 10million.txt 
item530284   plat530284
item7946579   plat7946579
item1521735   plat1521735
item9762844   plat9762844
item2289811   plat2289811
item6878181   plat6878181
item7957075   plat7957075
item2527811   plat2527811
item5940907   plat5940907
item3289494   plat3289494
$ TIMEFORMAT=%E
$ time sort 1million.txt >/dev/null
1.547
$ time sort 10million.txt >/dev/null
19.187

To 1,5 sekundy na 1 milion linii i 19 sekund na 10 milionów linii.

JoL
źródło
W takim przypadku lepiej byłoby przechowywać posortowane dane wejściowe w (tymczasowych) plikach pośrednich, ponieważ sortowanie trwa dość długo w przypadku zestawów danych o rozmiarach innych niż trywialne. W przeciwnym razie +1.
David Foerster,
@David To dobra uwaga. Osobiście nie podoba mi się tworzenie plików pośrednich, ale niecierpliwię się również na długotrwałe procesy. Zastanawiałem się, co by to było „trywialne”, więc zrobiłem mały test porównawczy i dodałem go do odpowiedzi wraz z twoją sugestią.
JoL
Sortowanie 1 mln rekordów jest wystarczająco szybkie na dość nowoczesnych komputerach stacjonarnych. Przy 2 kolejnych 3 rzędach wielkości więcej rzeczy zaczyna być interesujących. W każdym razie upływ czasu (w czasie) ( %Ew formacie czasu) jest mniej interesujący do mierzenia wydajności obliczeniowej. Czas procesora w trybie użytkownika ( %Ulub po prostu TIMEFORMATzmienna nieuzbrojona ) byłby znacznie bardziej znaczący.
David Foerster
@ David Nie jestem naprawdę zaznajomiony z przypadkami użycia dla różnych czasów. Dlaczego to jest bardziej interesujące? Upływający czas pokrywa się z czasem, na który tak naprawdę czekam. Dla polecenia 1,5 sekundy dostaję 4,5 sekundy %U.
JoL
1
Na upływ czasu wpływa czas oczekiwania na inne zadania uruchomione w tym samym systemie i blokujące żądania We / Wy. (Użytkownik) Czas procesora nie jest. Zwykle porównując szybkość algorytmów związanych z obliczeniami, chce się pominąć operacje we / wy i uniknąć błędów pomiaru spowodowanych innymi zadaniami w tle. Ważne pytanie brzmi: „Ile obliczeń wymaga ten algorytm dla tego zestawu danych?” zamiast „Ile czasu mój komputer poświęcił na wszystkie swoje zadania, czekając na zakończenie obliczeń?”
David Foerster