Bandingkan dua file baris demi baris dan buat perbedaan di file lain

121

Saya ingin membandingkan file1 dengan file2 dan menghasilkan file3 yang berisi baris di file1 yang tidak ada di file2.

Balualways
sumber
Saya mencoba diff tetapi menghasilkan beberapa angka dan simbol lain di depan baris berbeda yang membuat saya sulit untuk membandingkan file.
Minggu

Jawaban:

216

diff (1) bukanlah jawabannya, tetapi comm (1) adalah.

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

Begitu

comm -2 -3 file1 file2 > file3

File masukan harus diurutkan. Jika tidak, urutkan terlebih dahulu. Ini dapat dilakukan dengan file sementara, atau ...

comm -2 -3 <(sort file1) <(sort file2) > file3

asalkan shell Anda mendukung substitusi proses (bash mendukungnya).

sorpigal
sumber
1
Ingatlah bahwa dua file harus diurutkan dan unik
andy
6
Anda dapat mengelompokkan opsi bersama:comm -23
Paolo M
Apa artinya "diurutkan"? Bahwa garis memiliki urutan yang sama? Maka itu mungkin baik-baik saja untuk sebagian besar kasus penggunaan - seperti, memeriksa baris apa yang telah ditambahkan dengan membandingkan dengan versi cadangan yang lebih lama. Jika garis yang baru ditambahkan tidak dapat berada di antara garis yang ada, itu lebih menjadi masalah.
Egor Hans
@EgorHans: jika file memiliki misalnya baris yang berisi bilangan bulat seperti "3 \ n1 \ n3 \ n2 \ n" baris harus terlebih dahulu diurutkan ulang ke dalam urutan naik atau turun misalnya "\ 1 \ n2 \ n3 \ n3 \ n" dengan duplikat berdekatan. Itu adalah "diurutkan" dan kedua file harus diurutkan dengan cara yang sama. Ketika file yang lebih baru memiliki baris baru, tidak masalah jika mereka "di antara baris yang ada" karena setelah diurutkan, mereka dalam urutan yang diurutkan.
sorpigal
48

Utilitas Unix diffdimaksudkan untuk tujuan ini.

$ diff -u file1 file2 > file3

Lihat manual dan Internet untuk opsi, format keluaran yang berbeda, dll.

Thanatos
sumber
8
Itu tidak melakukan pekerjaan yang diminta; itu memasukkan banyak karakter tambahan, bahkan dengan penggunaan saklar baris perintah yang disarankan dalam jawaban lain.
xenocyon
20

Pertimbangkan ini:
file a.txt:

abcd
efgh

file b.txt:

abcd

Anda dapat menemukan perbedaannya dengan:

diff -a --suppress-common-lines -y a.txt b.txt

Outputnya adalah:

efgh 

Anda dapat mengubah output dalam file output (c.txt) menggunakan:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

Ini akan menjawab pertanyaan Anda:

"... yang berisi baris di file1 yang tidak ada di file2."

Neilvert Noval
sumber
2
Ada dua batasan untuk jawaban ini: (1) ini hanya berfungsi untuk baris pendek (kurang dari 80 karakter secara default, meskipun ini dapat dimodifikasi) dan, yang lebih penting, (2) menambahkan "<" di akhir setiap baris yang harus dihapus dengan program lain (mis. awk, sed).
sergut
Dalam banyak kasus, Anda juga ingin menggunakan -d, yang akan diffberusaha sebaik mungkin untuk menemukan perbedaan sekecil mungkin. -i, -E, -w, -BDan --suppress-blank-emptyjuga dapat berguna kadang-kadang, meskipun tidak selalu. Jika Anda tidak tahu apa yang cocok dengan kasus penggunaan Anda, coba diff --helpdulu (yang umumnya merupakan ide yang bagus ketika Anda tidak tahu apa yang dapat dilakukan sebuah perintah).
Egor Hans
Juga, dengan menggunakan --line-format =% L, Anda menjaga diff agar tidak menghasilkan karakter tambahan (setidaknya, bantuan mengatakan itu berfungsi seperti ini, namun akan mencobanya).
Egor Hans
Juga ini lebih pendek dan tampaknya berfungsi sama stackoverflow.com/a/27667185/1179925
mrgloom
8

Terkadang diffutilitas yang Anda butuhkan, tetapi terkadang joinlebih tepat. File-file tersebut perlu disortir sebelumnya atau, jika Anda menggunakan shell yang mendukung substitusi proses seperti bash, ksh atau zsh, Anda dapat melakukan pengurutan dengan cepat.

join -v 1 <(sort file1) <(sort file2)
Dijeda sampai pemberitahuan lebih lanjut.
sumber
Anda harus mendapatkan medali untuk ini! Itu persis seperti yang saya cari selama 2 jam terakhir
Zatarra
7

Mencoba

sdiff file1 file2

Ini biasanya bekerja jauh lebih baik dalam banyak kasus bagi saya. Anda mungkin ingin mengurutkan file sebelumnya, jika urutan baris tidak penting (mis. Beberapa file konfigurasi teks).

Sebagai contoh,

sdiff -w 185 file1.cfg file2.cfg
Tagar
sumber
1
Utilitas yang bagus! Saya suka bagaimana itu menandai garis pembeda. Membuat lebih mudah untuk membandingkan konfigurasi. Ini bersama-sama dengan jenis adalah kombo yang mematikan (misalnya sdiff <(sort file1) <(sort file2))
jmagnusson
3

Jika Anda perlu menyelesaikan ini dengan coreutils, jawaban yang diterima adalah baik:

comm -23 <(sort file1) <(sort file2) > file3

Anda juga dapat menggunakan sd (stream diff), yang tidak memerlukan pengurutan atau substitusi proses dan mendukung aliran tak terbatas, seperti:

cat file1 | sd 'cat file2' > file3

Mungkin tidak banyak manfaat dari contoh ini, tapi tetap pertimbangkan; dalam beberapa kasus, Anda tidak akan dapat menggunakan commatau grep -Ftidak diff.

Berikut adalah posting blog yang saya tulis tentang aliran diffing di terminal, yang memperkenalkan sd.

mlg
sumber
3

Namun, tidak ada grepsolusi?

  • baris yang hanya ada di file2:

    grep -Fxvf file1 file2 > file3
  • baris yang hanya ada di file1:

    grep -Fxvf file2 file1 > file3
  • baris yang ada di kedua file:

    grep -Fxf file1 file2 > file3
αғsнιη
sumber
2

Banyak jawaban sudah, tapi tidak satupun dari mereka IMHO sempurna. Jawaban Thanatos menyisakan beberapa karakter tambahan per baris dan jawaban Sorpigal mengharuskan file diurutkan atau disortir sebelumnya, yang mungkin tidak memadai di semua situasi.

Saya pikir cara terbaik untuk mendapatkan garis yang berbeda dan tidak ada yang lain (tidak ada karakter tambahan, tidak ada re-pemesanan) adalah kombinasi dari diff, grep, dan awk(atau serupa).

Jika baris tidak mengandung "<", satu baris pendek dapat berupa:

diff urls.txt* | grep "<" | sed 's/< //g'

tetapi itu akan menghapus setiap contoh "<" (kurang dari, spasi) dari baris, yang tidak selalu OK (misalnya kode sumber). Opsi teraman adalah menggunakan awk:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

Satu baris ini membedakan kedua file, lalu memfilter keluaran gaya-ed dari diff, lalu menghapus tanda "<" yang ditambahkan diff. Ini berfungsi bahkan jika baris berisi beberapa "<" itu sendiri.

sergut
sumber
1
comm tidak memerlukan penyortiran (dalam versi yang lebih baru?) - cukup gunakan --nocheck-order. Saya sering menggunakan ini saat memanipulasi csvs dari CLI
ak5
2

Saya terkejut tidak ada yang menyebutkan diff -yuntuk menghasilkan output berdampingan , misalnya:

diff -y file1 file2 > file3

Dan di file3(baris yang berbeda memiliki simbol |di tengah):

same     same
diff_1 | diff_2
xtluo.dll
sumber
1

Gunakan utilitas Diff dan ekstrak hanya baris yang dimulai dengan <di output

Capslockk
sumber
0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

Saya mencoba hampir semua jawaban di utas ini, tetapi tidak ada yang lengkap. Setelah beberapa jalur di atas, satu berhasil untuk saya. diff akan memberi Anda perbedaan tetapi dengan beberapa karakter khusus yang tidak diinginkan. di mana Anda garis perbedaan yang sebenarnya dimulai dengan '>'. jadi langkah selanjutnya adalah grep baris dimulai dengan '>' dan diikuti dengan menghapus yang sama dengan sed .

tollin jose
sumber
1
Ini ide yang buruk. Anda juga perlu mengubah baris yang dimulai dengan <. Anda akan melihat ini jika Anda menukar urutan file input. Bahkan jika Anda melakukan ini, Anda ingin menghilangkan grepdengan menggunakan lebih banyak sed: `diff a1 a2 | sed '/> / s ///' `` Ini masih dapat memutus baris yang berisi >atau <dalam situasi yang benar dan masih menyisakan baris tambahan yang menjelaskan nomor baris. Jika Anda ingin mencoba pendekatan ini cara yang lebih baik akan menjadi: diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'.
sorpigal
0

Anda bisa menggunakan diff dengan format keluaran berikut:

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format='', nonaktifkan output untuk file1 jika baris berbeda dibandingkan di file2.
--unchanged-line-format='', nonaktifkan keluaran jika garisnya sama.

αғsнιη
sumber