Temukan file yang ada di satu direktori tetapi tidak di yang lain [ditutup]

295

Saya mencoba mencari file yang ada di satu direktori tetapi tidak di yang lain, saya mencoba menggunakan perintah ini:

diff -q dir1 dir2

Masalah dengan perintah di atas bahwa ia menemukan kedua file dalam dir1tetapi tidak dalam dir2serta file dalam dir2tetapi tidak dalam dir1,

Saya mencoba mencari file di dir1tetapi tidak dir2hanya di .

Berikut ini sedikit contoh tampilan data saya

dir1    dir2    dir3
1.txt   1.txt   1.txt
2.txt   3.txt   3.txt
5.txt   4.txt   5.txt
6.txt   7.txt   8.txt

Pertanyaan lain dalam benak saya adalah bagaimana saya dapat menemukan file dir1tetapi tidak dalam dir2atau dir3dalam satu perintah?

Kesalahan 404
sumber

Jawaban:

390
diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt

Penjelasan:

  • diff -r dir1 dir2 menunjukkan file mana yang hanya di dir1 dan yang hanya di dir2 dan juga perubahan dari file yang ada di kedua direktori jika ada.

  • diff -r dir1 dir2 | grep dir1 menunjukkan file mana yang hanya dalam dir1

  • awk untuk mencetak hanya nama file.

asclepix
sumber
5
Saya akan grepuntuk STh seperti ^dir1untuk memastikan saya tidak mendapatkan dir1muncul kemudian di jalan.
Alfe
@Alfe Dapat ditingkatkan. Saya gunakan $4sebagai contoh. Faktanya, pada Ubuntu saya yang sebenarnya, diffbalasan dalam bahasa Italia. $4ok untuk balasan Italia dan Inggris, tapi saya tidak yakin untuk setiap bahasa lain ...
asclepix
139

Ini harus melakukan pekerjaan:

diff -rq dir1 dir2

Opsi menjelaskan (melalui halaman manual diff (1) ):

  • -r - Secara rekursif membandingkan subdirektori yang ditemukan.
  • -q - Keluaran hanya apakah file berbeda.
tokhi
sumber
8
Bagus! Tapi saya pikir itu harus diperluas seperti itu:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch
2
Ini perbandingan berdasarkan konten, tetapi mungkin membutuhkan waktu lama pada drive yang lambat.
Smeterlink
5
Hanya catatan pada -qopsi: Halaman manual hanya mengatakan "Keluarkan hanya apakah file berbeda", bukan bagaimana memeriksa apakah mereka berbeda. Saya meneliti kode sumber dan menemukan bahwa itu hanya memeriksa ukuran file untuk menentukan perbedaan, bukan konten yang sebenarnya.
ryancdotnet
Mengenai -qopsi saya tidak dapat mereproduksi bahwa itu hanya memeriksa ukuran file. Menggunakan GNU Diffutils 3.7 membandingkan dua file dengan ukuran file yang sama tetapi konten berbeda dengan diff -q file1 file2output Files file1 and file2 differ.
Stefan Schmidt
50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Perintah ini akan memberi Anda file yang ada di dir1 dan bukan di dir2.

Tentang <( )tanda, Anda dapat google sebagai 'proses substitusi'.

plhn
sumber
itu akan baik-baik saja untuk bekerja juga dengan subdirektori, saya pikir (ls -R dir1|sort)bisa melakukan trik
ulkas
1
Ini akan bekerja pada mode pemulihan OS X.
Anthony Vanover
@ulkas, hasilnya bisa salah jika Anda menggunakan (ls -R dir|sort).
Andriy Makukha
3
vimdiff memberikan perbandingan visual yang jauh lebih bagus dengan penyorotan warna:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed
32

Cara yang baik untuk melakukan perbandingan ini adalah dengan menggunakan finddengan md5sum, maka diff.

Contoh:

Gunakan finduntuk membuat daftar semua file di direktori kemudian menghitung hash md5 untuk setiap file dan pipa ke file:

find /dir1/ -type f -exec md5sum {} \; > dir1.txt

Lakukan prosedur yang sama ke direktori lain:

find /dir2/ -type f -exec md5sum {} \; > dir2.txt

Kemudian bandingkan hasil dua file dengan "diff":

diff dir1.txt dir2.txt

Strategi ini sangat berguna ketika dua direktori yang akan dibandingkan tidak berada di mesin yang sama dan Anda perlu memastikan bahwa file-file tersebut sama di kedua direktori.

Cara lain yang baik untuk melakukan pekerjaan itu adalah menggunakan git

git diff --no-index dir1/ dir2/

Salam Hormat!

Adail Junior
sumber
1
Aku tidak pergi git bisa melakukan diff pada direktori arbitrer yang tidak ada dalam repo git ... mengagumkan !!! Jawaban ini baru saja memecahkan masalah besar bagi saya, terima kasih
ViktorNova
17

Meld ( http://meldmerge.org/ ) melakukan pekerjaan yang baik dalam membandingkan direktori dan file di dalamnya.

Meld membandingkan direktori

Catalin Hritcu
sumber
Kecuali berbaur melakukan pekerjaan yang buruk ketika datang ke akhir baris ...
0xC0000022L
1
Tidak pernah memiliki masalah dengan akhiran garis. Bisakah Anda menjelaskan?
Catalin Hritcu
Ya, itu tidak menunjukkan akhir baris. Ini telah (berulang kali) menyebabkan pengembang menggunakan alat ini melakukan perubahan yang "memperbaiki" ujung jalur dengan membuat CRLF menjadi CRLFLF, misalnya.
0xC0000022L
3
Itu juga bersikeras membaca konten file, dan karena itu hampir tidak berguna dengan >> 1GB direktori.
Tomislav Nakic-Alfirevic
13

Plugin DirDiff vim adalah alat lain yang sangat berguna untuk membandingkan direktori.

vim -c "DirDiff dir1 dir2"

Itu tidak hanya daftar file mana yang berbeda antara direktori, tetapi juga memungkinkan Anda untuk memeriksa / memodifikasi dengan vimdiff file yang berbeda.

drrossum
sumber
11

Tidak puas dengan semua balasan, karena kebanyakan dari mereka bekerja sangat lambat dan menghasilkan keluaran panjang yang tidak perlu untuk direktori besar, saya menulis skrip Python saya sendiri untuk membandingkan dua folder.

Tidak seperti banyak solusi lain, itu tidak membandingkan konten file. Juga tidak masuk ke dalam subdirektori yang hilang di direktori lain. Jadi hasilnya cukup ringkas dan skripnya bekerja dengan cepat.

#!/usr/bin/env python3

import os, sys

def compare_dirs(d1: "old directory name", d2: "new directory name"):
    def print_local(a, msg):
        print('DIR ' if a[2] else 'FILE', a[1], msg)
    # ensure validity
    for d in [d1,d2]:
        if not os.path.isdir(d):
            raise ValueError("not a directory: " + d)
    # get relative path
    l1 = [(x,os.path.join(d1,x)) for x in os.listdir(d1)]
    l2 = [(x,os.path.join(d2,x)) for x in os.listdir(d2)]
    # determine type: directory or file?
    l1 = sorted([(x,y,os.path.isdir(y)) for x,y in l1])
    l2 = sorted([(x,y,os.path.isdir(y)) for x,y in l2])
    i1 = i2 = 0
    common_dirs = []
    while i1<len(l1) and i2<len(l2):
        if l1[i1][0] == l2[i2][0]:      # same name
            if l1[i1][2] == l2[i2][2]:  # same type
                if l1[i1][2]:           # remember this folder for recursion
                    common_dirs.append((l1[i1][1], l2[i2][1]))
            else:
                print_local(l1[i1],'type changed')
            i1 += 1
            i2 += 1
        elif l1[i1][0]<l2[i2][0]:
            print_local(l1[i1],'removed')
            i1 += 1
        elif l1[i1][0]>l2[i2][0]:
            print_local(l2[i2],'added')
            i2 += 1
    while i1<len(l1):
        print_local(l1[i1],'removed')
        i1 += 1
    while i2<len(l2):
        print_local(l2[i2],'added')
        i2 += 1
    # compare subfolders recursively
    for sd1,sd2 in common_dirs:
        compare_dirs(sd1, sd2)

if __name__=="__main__":
    compare_dirs(sys.argv[1], sys.argv[2])

Penggunaan sampel:

user@laptop:~$ python3 compare_dirs.py dir1/ dir2/
DIR  dir1/out/flavor-domino removed
DIR  dir2/out/flavor-maxim2 added
DIR  dir1/target/vendor/flavor-domino removed
DIR  dir2/target/vendor/flavor-maxim2 added
FILE dir1/tmp/.kconfig-flavor_domino removed
FILE dir2/tmp/.kconfig-flavor_maxim2 added
DIR  dir2/tools/tools/LiveSuit_For_Linux64 added

Atau jika Anda hanya ingin melihat file dari direktori pertama:

user@laptop:~$ python3 compare_dirs.py dir2/ dir1/ | grep dir1
DIR  dir1/out/flavor-domino added
DIR  dir1/target/vendor/flavor-domino added
FILE dir1/tmp/.kconfig-flavor_domino added

PS Jika Anda perlu membandingkan ukuran file dan hash file untuk kemungkinan perubahan, saya menerbitkan skrip yang diperbarui di sini: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

Andriy Makukha
sumber
Script cukup sederhana yang melakukan persis apa yang saya inginkan: Verifikasi salinan massal: +1 dari saya. (perlu mengkonversi ke python2) Petunjuk: penggunaan set mungkin membuat bagian diff lebih sederhana.
Jason Morgan
6

Pendekatan lain (mungkin lebih cepat untuk direktori besar):

$ find dir1 | sed 's,^[^/]*/,,' | sort > dir1.txt && find dir2 | sed 's,^[^/]*/,,' | sort > dir2.txt
$ diff dir1.txt dir2.txt

The sedperintah menghapus pertama komponen direktori berkat posting Erik`s )

jaltek
sumber
1
Saya percaya metode ini lebih sederhana (masih menggunakan findkarenanya komentar dan bukan jawaban yang terpisah): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Ini akan mencetak file yang ada di dir2 tetapi tidak ada di dir1.
Alexander Amelkin
5

Ini agak terlambat tetapi dapat membantu seseorang. Tidak yakin apakah diff atau rsync hanya memuntahkan nama file dalam format kosong seperti ini. Terima kasih kepada plhn karena memberikan solusi bagus yang saya kembangkan di bawah.

Jika Anda hanya ingin nama file sehingga mudah untuk menyalin file yang Anda butuhkan dalam format bersih, Anda dapat menggunakan perintah find.

comm -23 <(find dir1 | sed 's/dir1/\//'| sort) <(find dir2 | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Ini mengasumsikan bahwa kedua dir1 dan dir2 berada di folder induk yang sama. sed hanya menghapus folder induk sehingga Anda dapat membandingkan apel dengan apel. Sed terakhir hanya mengembalikan nama dir1.

Jika Anda hanya ingin file:

comm -23 <(find dir1 -type f | sed 's/dir1/\//'| sort) <(find dir2 -type f | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Demikian pula untuk direktori:

comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
James
sumber
1
Perhatikan bahwa Anda bisa melakukan cdsebelum finddaripada harus digunakan sed, misalnya: comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). ( exitS di sini untuk mencegah findpenggunaan direktori saat ini harus cdgagal.)
phk
Juga perhatikan bahwa solusi Anda mungkin gagal ketika file dengan karakter khusus tertentu hadir, jika Anda memiliki versi terbaru commdengan dukungan -z(datang dengan git.savannah.gnu.org/cgit/coreutils.git/commit/… ) yang dapat Anda lakukan comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (Sementara itu saya juga tahu bahwa exit
huruf
5

Jawaban yang diterima juga akan mencantumkan file yang ada di kedua direktori, tetapi memiliki konten yang berbeda. Untuk daftar HANYA file yang ada di dir1 Anda dapat menggunakan:

diff -r dir1 dir2 | grep 'Only in' | grep dir1 | awk '{print $4}' > difference1.txt

Penjelasan:

  • diff -r dir1 dir2: bandingkan
  • grep 'Only in': dapatkan baris yang berisi 'Only in'
  • grep dir1: dapatkan baris yang berisi dir
Aris
sumber
5

Jawaban ini mengoptimalkan salah satu saran dari @ Adail-Junior dengan menambahkan -Dopsi, yang bermanfaat ketika tidak ada direktori yang dibandingkan adalah repositori git:

git diff -D --no-index dir1/ dir2/

Jika Anda menggunakan -Dmaka Anda tidak akan melihat perbandingan untuk /dev/null: text Binary files a/whatever and /dev/null differ

Mike Slinn
sumber
Sangat berguna dalam membandingkan dua direktori, Anda langsung melihat perbedaan antara file. Tentu saja berfungsi paling baik pada file dengan konten teks.
Erich Kuester
1

Cara yang disederhanakan untuk membandingkan 2 direktori menggunakan perintah DIFF

diff filename.1 filename.2> filename.dat >> Enter

buka filename.dat setelah proses selesai

dan Anda akan melihat: Hanya dalam nama file.1: nama file.2 Hanya di: directory_name: name_of_file1 Hanya di: directory_Name: name_of_file2

nerakk
sumber
Mengapa Anda harus meng-output ke file dat?
Wisnu NK
1

Ini adalah skrip bash untuk mencetak perintah untuk menyinkronkan dua direktori

dir1=/tmp/path_to_dir1
dir2=/tmp/path_to_dir2
diff -rq $dir1 $dir2 | sed -e "s|Only in $dir2\(.*\): \(.*\)|cp -r $dir2\1/\2 $dir1\1|" |  sed -e "s|Only in $dir1\(.*\): \(.*\)|cp -r $dir1\1/\2 $dir2\1|" 
Ebru Yener
sumber
0

GNU grepdapat membalikkan pencarian dengan opsi -v. Ini membuat greppelaporan garis, yang tidak cocok. Dengan ini, Anda dapat menghapus file dir2dari daftar file di dir1.

grep -v -F -x -f <(find dir2 -type f -printf '%P\n') <(find dir1 -type f -printf '%P\n')

Opsi -F -xmemberi tahu grepuntuk melakukan pencarian string di seluruh baris.

ceving
sumber