bandingkan dua kolom file yang berbeda dan cetak jika cocok

16

Saya menggunakan Solaris 10 dan opsi grep yang melibatkan -f tidak bekerja.

Saya memiliki dua file yang dipisahkan pipa:

file1:

abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

file 2:

abc|123|
kumar|pki|
cab|234

Saya ingin membandingkan dua kolom pertama dari file2 dengan file1 (mencari seluruh isi file1 dalam dua kolom pertama) jika mereka cocok mencetak baris yang cocok dari file1. Kemudian cari baris kedua file 2 dan seterusnya.

Output yang Diharapkan:

abc|123|BNY|apple|
cab|234|cyx|orange|

File yang saya miliki sangat besar, berisi sekitar 400.000 baris, jadi saya ingin mempercepat eksekusi.

pengguna68365
sumber
Saya menghapus spasi utama dari contoh Anda, jika Anda menginginkannya, silakan putar kembali sunting. Ingat bahwa spasi itu penting, Anda hanya boleh memilikinya jika ada di file Anda yang sebenarnya.
terdon
Coba gunakan versi GNU dari grep, itu di bawah /usr/sfw/bin/ggrep. stackoverflow.com/questions/15259882/…
slm

Jawaban:

21

Inilah yang dirancang untuk awk:

$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|

Penjelasan

  • -F'|': mengatur pemisah bidang ke |.
  • NR==FNR: NR adalah nomor baris input saat ini dan FNR nomor baris file saat ini. Keduanya akan sama hanya saat file 1 sedang dibaca.
  • c[$1$2]++; next: jika ini adalah file ke-1, simpan kedua bidang ke-1 dalam carray. Kemudian, lewati ke baris berikutnya sehingga ini hanya diterapkan pada file ke-1.

  • c[$1$2]>0: blok else hanya akan dieksekusi jika ini adalah file kedua jadi kami memeriksa apakah bidang 1 dan 2 dari file ini sudah terlihat ( c[$1$2]>0) dan jika sudah, kami mencetak baris. Dalam awk, tindakan standar adalah mencetak garis jadi jika c[$1$2]>0benar, garis akan dicetak.


Atau, karena Anda ditandai dengan Perl:

perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
         while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1

Penjelasan

Baris pertama akan terbuka file2, membaca semuanya hingga ke-2 |( .+?\|[^|]+) dan menyimpannya (itu $&adalah hasil dari operator pertandingan terakhir) di %khash.

Baris kedua memproses file1, menggunakan regex yang sama untuk mengekstrak dua kolom pertama dan mencetak baris jika kolom tersebut didefinisikan dalam %khash.


Kedua pendekatan di atas perlu menahan 2 kolom pertama file2 dalam memori. Itu seharusnya tidak menjadi masalah jika Anda hanya memiliki beberapa ratus ribu baris tetapi jika ya, Anda bisa melakukan sesuatu seperti

cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done

Tapi itu akan lebih lambat.

terdon
sumber
Tapi bukankah ini akan memuat semua (dua kolom pertama) file2ke dalam memori?
Joseph R.
@terdon: awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0'adalah versi yang lebih pendek.
cuonglm
itu tidak berfungsi ..
user68365
@ user68365: Apakah file2memiliki baris rangkap?
cuonglm
TIDAK tidak memiliki baris duplikat
user68365
1

kupikir

grep -Ff file2 file1

adalah apa yang kamu cari. Itu harus efisien, tetapi saya tidak yakin itu akan seakurat yang Anda inginkan. Jika abc|123(misalnya) ditemukan garis dalam file1kolom yang berbeda, garis itu akan dicetak juga. Jika Anda dapat menjamin bahwa ini tidak akan pernah terjadi, kalimat di atas akan berfungsi.

Joseph R.
sumber
Grep tidak akan cukup, karena abc | 123 mungkin ada di suatu tempat di file th. Selain itu saya menggunakan solaris 10 dan saya tidak dapat menggunakan opsi grep juga.
user68365
2
@ user68365 tolong jelaskan semua ini dalam pertanyaan Anda. Anda perlu memberi tahu kami OS Anda dan menentukan bahwa Anda hanya ingin mencocokkan 2 kolom pertama.
terdon
1

Jika Anda ingin memikirkan masalah dalam SQL like way, maka Anda pasti harus mencoba alat bernama ' q ':

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

Lebih jelas dan mudah dipahami jika Anda terbiasa dengan query SQL.

Vincent
sumber
Terima kasih untuk salah satu solusi yang paling samar, sejauh ini. Itu yang saya mau. Tapi saya kesulitan menemukan "alat q" ini
Rolf
Alat yang sangat berguna.
ghilesZ
0
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|
mr_tron
sumber
1
Seperti yang telah saya edit dan sebutkan dalam pertanyaan, opsi grep -f tidak bekerja di sistem saya
user68365
Solaris 10 memiliki gnu core-utils di / usr / sfw / bin Gunakan / usr / sfw / bin / sed dan / usr / sfw / bin / grep
mr_tron