Combina due file con awk


9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Produzione desiderata:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Come posso farlo?

Risposte:


11

La risposta di seguito si basa su domande e risposte simili in SO con alcune modifiche rilevanti:

$ 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

L'idea è di creare una mappa hash con indice e usarla come dizionario.

Per la seconda domanda che hai posto nel tuo commento ( cosa dovrebbe essere cambiato se la seconda colonna file1.txtsarà la sesta colonna ):

Se il file di input sarà simile 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

Il seguente comando lo farà:

$ 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    

1
@pawana - Ho aggiornato la mia risposta per risolvere anche la tua seconda domanda in commento. Se ho risposto alla tua domanda, accettala .
Yaron,

6

So che hai detto awk, ma joina questo scopo esiste un comando ...

{
  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

Sarebbe sufficiente con il primo joincomando se non fosse per questa riga:

item4   platD

Il comando dice sostanzialmente: join basato sulla seconda colonna del primo file ( -1 2) e sulla prima colonna del secondo file ( -2 1), e genera la prima colonna del primo file e la seconda colonna del secondo file ( -o 1.1,2.2). Ciò mostra solo le linee accoppiate. Il secondo comando join dice quasi la stessa cosa, ma dice di mostrare le righe del primo file che non possono essere accoppiate ( -v 1) e di generare la prima colonna del primo file e la seconda colonna del primo file ( -o 1.1,1.2). Quindi ordiniamo l'output di entrambi combinati. sort -k 1significa ordinare in base alla prima colonna e sort -k 2significa ordinare in base alla seconda. È importante ordinare i file in base alla colonna di join prima di passarli a join.

Ora, ho scritto l'ordinamento due volte, perché non mi piace sporcare le mie directory di file se posso aiutarlo. Tuttavia, come ha detto David Foerster, a seconda della dimensione dei file, potresti voler ordinare i file e salvarli prima di non aspettare di ordinarli due volte. Per dare un'idea delle dimensioni, ecco il tempo necessario per ordinare 1 milione e 10 milioni di righe sul mio computer:

$ 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

Sono 1,5 secondi per 1 milione di linee e 19 secondi per 10 milioni di linee.


In questo caso sarebbe meglio archiviare i dati di input ordinati in file intermedi (temporanei) poiché l'ordinamento impiega abbastanza tempo per set di dati di dimensioni non banali. Altrimenti +1.
David Foerster,

@ David È un buon punto. Personalmente, non mi piace molto dover creare file intermedi, ma sono anche impaziente con i processi di lunga durata. Mi chiedevo quale sarebbe stato "di dimensioni insignificanti", quindi ho fatto un piccolo benchmark e l'ho aggiunto alla risposta insieme al tuo suggerimento.
JoL

Ordinare 1 milione di record è abbastanza veloce su computer desktop ragionevolmente moderni. Con altri 2 ordini di grandezza in più, 3 cose iniziano a diventare interessanti. In ogni caso, il tempo (reale) trascorso ( %Enel formato temporale) è meno interessante per misurare le prestazioni computazionali. Il tempo della CPU in modalità utente ( %Uo semplicemente una TIMEFORMATvariabile non impostata ) sarebbe molto più significativo.
David Foerster,

@ David Non ho molta familiarità con i casi d'uso per i diversi tempi. Perché è più interessante? Il tempo trascorso è ciò che coincide con il tempo che sto effettivamente aspettando. Per il comando di 1,5 secondi, ottengo 4,5 secondi con %U.
JoL

1
Il tempo trascorso è influenzato dal tempo trascorso in attesa di altre attività in esecuzione sullo stesso sistema e nel blocco delle richieste I / O. (Utente) Il tempo della CPU non lo è. Di solito, quando si confronta la velocità degli algoritmi associati al calcolo, si desidera ignorare l'I / O ed evitare errori di misurazione dovuti ad altre attività in background. La domanda importante è "Quanto calcolo richiede questo algoritmo su quel set di dati?" invece di "Quanto tempo ha trascorso il mio computer su tutte le sue attività mentre aspettava che quel calcolo fosse completato?"
David Foerster,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.