Saya memiliki dua file besar (set nama file). Sekitar 30.000 baris di setiap file. Saya mencoba menemukan cara cepat untuk menemukan baris di file1 yang tidak ada di file2.
Misalnya, jika ini file1:
line1
line2
line3
Dan ini file2:
line1
line4
line5
Maka hasil / output saya harus:
line2
line3
Ini bekerja:
grep -v -f file2 file1
Tapi itu sangat, sangat lambat saat digunakan pada file besar saya.
Saya menduga ada cara yang baik untuk melakukan ini menggunakan diff (), tetapi output harus hanya garis, tidak ada yang lain, dan saya tidak bisa menemukan saklar untuk itu.
Adakah yang bisa membantu saya menemukan cara cepat untuk melakukan ini, menggunakan bash dan binari linux dasar?
EDIT: Untuk menindaklanjuti pertanyaan saya sendiri, ini adalah cara terbaik yang saya temukan sejauh ini menggunakan diff ():
diff file2 file1 | grep '^>' | sed 's/^>\ //'
Tentunya harus ada cara yang lebih baik?
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt
cat file1 file2 file2 | sort | uniq --unique
lihat jawaban saya di bawah ini.Jawaban:
Anda dapat mencapai ini dengan mengontrol pemformatan baris lama / baru / tidak berubah dalam
diff
output GNU :File input harus disortir agar ini berfungsi. Dengan
bash
(danzsh
) Anda dapat mengurutkan di tempat dengan penggantian proses<( )
:Dalam baris baru dan tidak berubah di atas ditekan, sehingga hanya diubah (yaitu garis yang dihapus dalam kasus Anda) adalah output. Anda juga dapat menggunakan beberapa
diff
pilihan yang solusi lain tidak menawarkan, seperti-i
mengabaikan kasus, atau berbagai pilihan spasi (-E
,-b
,-v
dll) untuk pencocokan kurang ketat.Penjelasan
Opsi
--new-line-format
,--old-line-format
dan--unchanged-line-format
biarkan Anda mengontrol caradiff
memformat perbedaan, mirip denganprintf
penentu format. Opsi-opsi ini memformat masing-masing baris baru (ditambahkan), lama (dihapus) dan tidak berubah . Menyetel satu untuk mengosongkan "" mencegah keluaran dari jalur semacam itu.Jika Anda terbiasa dengan format diff terpadu , Anda dapat membuatnya sebagian dengan:
The
%L
specifier adalah garis yang bersangkutan, dan kami awalan masing-masing dengan "+" "-" atau "", sepertidiff -u
(catatan bahwa itu hanya perbedaan output, itu tidak memiliki---
+++
dan@@
garis-garis di bagian atas setiap perubahan dikelompokkan). Anda juga dapat menggunakan ini untuk melakukan hal-hal lain yang bermanfaat seperti jumlah setiap baris dengan%dn
.The
diff
Metode (bersama dengan saran laincomm
danjoin
) hanya menghasilkan output yang diharapkan dengan diurutkan masukan, meskipun Anda dapat menggunakan<(sort ...)
untuk mengurutkan di tempat. Berikutawk
skrip sederhana (nawk) (terinspirasi oleh skrip yang ditautkan ke dalam jawaban Konsolebox) yang menerima file input yang dipesan secara sewenang-wenang, dan menampilkan baris yang hilang sesuai urutannya pada file1.Ini menyimpan seluruh konten file1 baris demi baris dalam array yang diindeks nomor-baris
ll1[]
, dan seluruh konten file2 baris demi baris dalam array asosiatif yang diindeks baris-kontenss2[]
. Setelah kedua file dibaca, beralihlahll1
dan gunakanin
operator untuk menentukan apakah baris dalam file1 ada di file2. (Ini akan memiliki output yang berbeda dengandiff
metode ini jika ada duplikat.)Jika file-file tersebut cukup besar sehingga menyimpan keduanya menyebabkan masalah memori, Anda dapat menukar CPU dengan memori dengan hanya menyimpan file1 dan menghapus kecocokan sepanjang jalan saat file2 dibaca.
Di atas menyimpan seluruh isi file1 dalam dua array, satu diindeks dengan nomor baris
ll1[]
, satu diindeks oleh konten barisss1[]
. Kemudian saat file2 dibaca, setiap baris yang cocok dihapus darill1[]
danss1[]
. Pada akhirnya baris yang tersisa dari file1 adalah output, mempertahankan urutan asli.Dalam hal ini, dengan masalah seperti yang disebutkan, Anda juga dapat membagi dan menaklukkan menggunakan GNU
split
(pemfilteran adalah ekstensi GNU), dijalankan berulang dengan potongan file1 dan membaca file2 sepenuhnya setiap kali:Perhatikan penggunaan dan penempatan
-
maknastdin
padagawk
baris perintah. Ini disediakan olehsplit
dari file1 dalam potongan 20.000 baris per-doa.Untuk pengguna pada sistem non-GNU, ada hampir pasti coreutils GNU paket Anda dapat memperoleh, termasuk di OSX sebagai bagian dari Apel Xcode alat yang menyediakan GNU
diff
,awk
, meskipun hanya POSIX / BSDsplit
daripada versi GNU.sumber
diff
: secara umum file input akan berbeda, 1 dikembalikan olehdiff
dalam hal itu. Anggap itu bonus ;-) Jika Anda menguji dalam skrip shell 0 dan 1 adalah kode keluar yang diharapkan, 2 menunjukkan masalah.man diff
. Terima kasih!The comm perintah (singkatan dari "umum") mungkin berguna
comm - compare two sorted files line by line
The
man
file sebenarnya cukup mudah dibaca untuk ini.sumber
comm
juga memiliki opsi untuk memverifikasi input yang disortir,--check-order
(yang tampaknya tetap dilakukan, tetapi opsi ini akan menyebabkan kesalahan alih-alih melanjutkan). Tetapi untuk mengurutkan file, cukup lakukan:com -23 <(sort file1) <(sort file2)
dan seterusnyacomm
tidak berfungsi sama sekali. Perlu beberapa saat bagi saya untuk mengetahui bahwa ini tentang ujung garis: bahkan garis yang terlihat identik dianggap berbeda jika mereka memiliki ujung garis yang berbeda. Perintahdos2unix
ini dapat digunakan untuk mengubah ujung garis CRLF menjadi LF saja.Seperti konsolebox yang disarankan, solusi poster grep
sebenarnya berfungsi dengan baik (cepat) jika Anda hanya menambahkan
-F
opsi, untuk memperlakukan pola sebagai string tetap, bukan ekspresi reguler. Saya memverifikasi ini pada sepasang ~ 1000 daftar file baris yang harus saya bandingkan. Dengan-F
itu butuh 0,031 s (nyata), sementara tanpa itu butuh 2,278 s (nyata), ketika mengarahkan ulang keluaran grep kewc -l
.Tes-tes ini juga termasuk
-x
sakelar, yang merupakan bagian penting dari solusi untuk memastikan akurasi total dalam kasus-kasus di mana file2 berisi baris-baris yang cocok dengan bagian, tetapi tidak semua, satu atau lebih baris dalam file1.Jadi solusi yang tidak memerlukan input untuk diurutkan, cepat, fleksibel (sensitivitas huruf, dll) adalah:
Ini tidak berfungsi dengan semua versi grep, misalnya gagal di macOS, di mana baris dalam file 1 akan ditampilkan sebagai tidak ada di file 2, meskipun itu, jika cocok dengan baris lain yang merupakan substring dari itu . Atau Anda dapat menginstal GNU grep di macOS untuk menggunakan solusi ini.
sumber
-F
ini tidak skala baik.file2
.-x
opsi tersebut ternyata menggunakan lebih banyak memori. Denganfile2
180M kata yang berisi 6-10 byte proses sayaKilled
menggunakan mesin RAM 32GB ...Bagaimana kecepatan sebagai sort dan diff?
sumber
Jika Anda kekurangan "alat mewah", misalnya dalam beberapa distribusi Linux minimal, ada solusi dengan adil
cat
,sort
danuniq
:Uji:
Ini juga relatif cepat, dibandingkan dengan
grep
.sumber
--unique
opsi. Anda harus dapat menggunakan opsi POSIX standar untuk ini:| uniq -u
seq 1 1 7
membuat angka dari 1, dengan selisih 1, hingga 7, yaitu 1 2 3 4 5 6 7. Dan di sana ada 2 Anda!The
-t
memastikan bahwa itu membandingkan seluruh baris, jika Anda memiliki ruang dalam beberapa baris.sumber
comm
,join
mengharuskan kedua jalur input untuk diurutkan pada bidang di mana Anda menjalankan operasi gabungan.Anda dapat menggunakan Python:
sumber
Gunakan
combine
darimoreutils
paket, utilitas set yang mendukungnot
,and
,or
,xor
operasiyaitu memberi saya baris yang ada di file1 tetapi tidak di file2
ATAU beri saya baris dalam file1 baris minus di file2
Catatan:
combine
mengurutkan dan menemukan baris unik di kedua file sebelum melakukan operasi apa pun tetapidiff
tidak. Jadi, Anda mungkin menemukan perbedaan antara outputdiff
dancombine
.Jadi sebenarnya Anda katakan
Temukan baris yang berbeda di file1 dan file2 dan kemudian beri saya baris di file1 dikurangi baris di file2
Dalam pengalaman saya, ini jauh lebih cepat daripada opsi lain
sumber
Menggunakan fgrep atau menambahkan opsi -F untuk grep bisa membantu. Tetapi untuk perhitungan yang lebih cepat Anda bisa menggunakan Awk.
Anda dapat mencoba salah satu metode Awk ini:
http://www.linuxquestions.org/questions/programming-9/grep-for-huge-files-826030/#post4066219
sumber
Cara saya biasanya melakukan ini adalah menggunakan
--suppress-common-lines
flag, meskipun perhatikan bahwa ini hanya berfungsi jika Anda melakukannya dalam format berdampingan.diff -y --suppress-common-lines file1.txt file2.txt
sumber
Saya menemukan bahwa bagi saya menggunakan pernyataan normal jika dan untuk loop bekerja dengan sempurna.
sumber
grep
hasil Anda diperluas ke beberapa kata, atau jika salah satufile2
entri Anda dapat diperlakukan oleh shell sebagai gumpalan.