Sto usando Solaris 10 e quindi le opzioni grep che coinvolgono -f non funzionano.
Ho due file separati da pipe:
file1:
abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|
file 2:
abc|123|
kumar|pki|
cab|234
Vorrei confrontare le prime due colonne di file2 con file1 (cerca l'intero contenuto di file1 nelle prime due colonne) se corrispondono, stampa la riga corrispondente di file1. Quindi cerca la seconda riga del file 2 e così via.
Uscita prevista:
abc|123|BNY|apple|
cab|234|cyx|orange|
I file che ho sono enormi, contengono circa 400.000 righe, quindi vorrei velocizzare l'esecuzione.
Risposta accettata:
Questo è ciò per cui awk è stato progettato:
$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|
Spiegazione
-F'|'
:imposta il separatore di campo su|
.NR==FNR
:NR è il numero di riga di input corrente e FNR il numero di riga del file corrente. I due saranno uguali solo durante la lettura del primo file.-
c[$1$2]++; next
:se questo è il 1° file, salva i primi due campi nelc
Vettore. Quindi, passa alla riga successiva in modo che venga applicata solo al primo file. -
c[$1$2]>0
:il blocco else verrà eseguito solo se questo è il secondo file quindi controlliamo se i campi 1 e 2 di questo file sono già stati visti (c[$1$2]>0
) e se lo sono stati, stampiamo la riga. Inawk
, l'azione predefinita è stampare la riga così sec[$1$2]>0
è vero, la riga verrà stampata.
In alternativa, poiché hai taggato con Perl:
perl -e 'open(A, "file2"); while(<A>){/.+?|[^|]+/ && $k{$&}++};
while(<>){/.+?|[^|]+/ && do{print if defined($k{$&})}}' file1
Spiegazione
La prima riga aprirà file2
, leggi tutto fino al 2° |
(.+?|[^|]+
) e salvarlo (il $&
è il risultato dell'ultimo operatore di corrispondenza) nel %k
hash.
La seconda riga elabora file1, usa la stessa espressione regolare per estrarre le prime due colonne e stampare la riga se quelle colonne sono definite in %k
hash.
Entrambi gli approcci precedenti dovranno contenere le prime 2 colonne di file2 in memoria. Non dovrebbe essere un problema se hai solo poche centinaia di migliaia di righe, ma se lo è, potresti fare qualcosa del tipo
cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done
Ma sarà più lento.
Correlati:copiare tutti i programmi e i file installati su un disco rigido (che ha Windows 7 a 32 bit) e clonarlo/trasferirlo su un altro computer che ha Windows 7 a 64 bit?