Apakah ada alat untuk mendapatkan baris dalam satu file yang tidak ada di file lain?

Jawaban:

159

Iya. Alat standar grepuntuk mencari file untuk string teks dapat digunakan untuk mengurangi semua baris dalam satu file dari yang lain.

grep -F -x -v -f fileB fileA

Ini bekerja dengan menggunakan setiap baris dalam fileB sebagai pola ( -f fileB) dan memperlakukannya sebagai string polos untuk mencocokkan (bukan regex biasa) ( -F). Anda memaksa kecocokan terjadi pada seluruh baris ( -x) dan mencetak hanya garis yang tidak cocok ( -v). Karenanya Anda mencetak baris dalam fileA yang tidak berisi data yang sama dengan baris apa pun di fileB.

Kelemahan dari solusi ini adalah tidak memperhitungkan urutan baris dan jika input Anda memiliki garis duplikat di tempat yang berbeda, Anda mungkin tidak mendapatkan apa yang Anda harapkan. Solusi untuk itu adalah dengan menggunakan alat perbandingan nyata seperti diff. Anda bisa melakukan ini dengan membuat file diff dengan nilai konteks pada 100% dari baris dalam file, kemudian menguraikannya hanya untuk baris yang akan dihapus jika mengkonversi file A ke file B. (Perhatikan perintah ini juga menghapus memformat setelah mendapat garis yang benar.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
sumber
@ inderpreet99 -uArgumen huruf kecil sebenarnya mengambil parameter angka asalkan tidak diikuti oleh spasi. Keuntungan dari cara saya sebelumnya adalah bahwa ia akan bekerja dengan atau tanpa nilai, sehingga Anda dapat menggunakan sesuatu dalam rutinitas sub-perintah yang mengembalikan bukan output. Huruf besar '-U' di sisi lain membutuhkan argumen.
Caleb
hati-hati, grep -f adalah O (N ^ 2) Saya percaya: stackoverflow.com/questions/4780203/…
rogerdpack
1
yang diffpipa bekerja yang memperlakukan terima kasih.
Felipe Alvarez
Untuk menjelaskan masalah penyortiran, Anda bisa menggunakan subtitusi proses dalam perintah untuk memproses setiap file sebelum yang grepdiperlukan. Contoh:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro
@TonyCesaro Itu akan berfungsi jika kumpulan data Anda tidak spesifik pesanan dan duplikat tidak perlu diperhitungkan. Keuntungan menggunakan diffadalah bahwa posisi dalam file diperhitungkan.
Caleb
57

Jawabannya sangat tergantung pada jenis dan format file yang Anda bandingkan.

Jika file yang Anda bandingkan adalah file teks yang diurutkan, maka alat GNU yang ditulis oleh Richard Stallman dan Davide McKenzie disebut commdapat melakukan penyaringan yang Anda cari. Itu adalah bagian dari coreutils.

Contoh

Katakanlah Anda memiliki 2 file berikut:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Baris dalam file byang tidak ada dalam file a:

$ comm <(sort a) <(sort b) -3
    6
Seorang teman
sumber
1
+1 untuk disebutkan comm; sayangnya, commmemerlukan file yang diurutkan
Arcege
11
jadi urutkan mereka? comm <(sort a) <(sort b) -1 -2
Sirex
Ini adalah beberapa sintaks aneh. <()? Ini berhasil dan saya mengerti, tetapi apakah ada nama untuk keanehan ini?
mlissner
2
@mlissner <()juga dikenal sebagai proses substitusi .
miku
1
commawalnya ditulis sekitar tahun 1973 oleh seseorang di Bell Labs, bukan rms. Anda merujuk pada implementasi GNU yang datang jauh kemudian. Ada banyak implementasi berbeda dari utilitas Unix sepanjang tahun.
Stéphane Chazelas
32

dari stackoverflow ...

comm -23 file1 file2

-23 menekan garis yang ada di kedua file, atau hanya di file 2. File harus diurutkan (mereka ada dalam contoh Anda) tetapi jika tidak, pipa mereka melalui sortir terlebih dahulu ...

Lihat halaman manual di sini

JJS
sumber
Ini tidak bekerja untuk saya, untuk beberapa alasan ...
Jan
@Jan apakah file Anda diurutkan? Bagaimana Anda menyortirnya?
JJS
8

Metode grep dan comm (dengan sortir) membutuhkan waktu lama pada file besar. SiegeX dan ghostdog74 membagikan dua metode awk yang hebat untuk mengekstraksi baris yang unik ke salah satu dari dua file di Stack Overflow:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
sumber
2
Jika Anda melakukan ini dengan file besar, maka kendala memori memuat file besar ke dalam array asosiatif akan menjadi penghalang.
Charles Duffy
4

Jika file berukuran besar dan Anda tidak memiliki pesanan khusus untuk entri Anda, grep membutuhkan waktu terlalu lama. Alternatif cepat adalah

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[hasil file2-file1 ke layar, pipa ke file dll]

Mengubah >ke <akan mendapatkan pengurangan yang berlawanan.rm 1 2

Eshel Faraggi
sumber
2

Anda juga dapat mempertimbangkan vimdiff, ini menyoroti perbedaan antara file dalam editor vim

simona
sumber
1
Tetapi adakah cara mudah untuk secara otomatis melakukan pengurangan dalam Vimdiff?
Kazark