Trova i file esistenti in una directory ma non nell'altra [chiuso]


295

Sto cercando di trovare i file esistenti in una directory ma non nell'altra, ho provato a usare questo comando:

diff -q dir1 dir2

Il problema con il comando precedente è che trova sia i file in dir1ma non in dir2così come i file in dir2ma non in dir1,

Sto cercando di trovare i file dir1ma non dir2solo.

Ecco un piccolo esempio di come sono i miei dati

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

Un'altra domanda che ho in mente è come posso trovare i file dir1ma non in dir2o dir3in un singolo comando?

Risposte:


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

Spiegazione:

  • diff -r dir1 dir2 mostra quali file sono solo in dir1 e quelli solo in dir2 e anche le eventuali modifiche dei file presenti in entrambe le directory.

  • diff -r dir1 dir2 | grep dir1 mostra quali file sono solo in dir1

  • awk per stampare solo il nome file.


5
grepSth mi piacerebbe ^dir1assicurarmi di non dir1apparire più avanti nel percorso.
Alfe,

@Alfe Può essere migliorato. Uso $4come esempio. In effetti, sul mio attuale Ubuntu, diffrisposte in italiano. $4va bene per le risposte in italiano e in inglese, ma non sono sicuro per tutte le altre lingue ...
asclepix,

139

Questo dovrebbe fare il lavoro:

diff -rq dir1 dir2

Opzioni spiegate (tramite la pagina man diff (1) ):

  • -r - Confronta ricorsivamente tutte le sottodirectory trovate.
  • -q - Emette solo se i file differiscono.

8
Bello! Ma penso che dovrebbe essere esteso in questo modo:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch

2
Questo è un confronto per contenuto, ma potrebbe richiedere molto tempo su unità lente.
Smeterlink

5
Solo una nota -qsull'opzione: le pagine man dicono solo "Output solo se i file differiscono", non come controlla se sono diversi. Ho esaminato il codice sorgente e ho scoperto che controlla solo le dimensioni del file per determinare le differenze, non i contenuti effettivi.
ryancdotnet,

Per quanto riguarda l' -qopzione, non riesco a riprodurre che controlla solo le dimensioni del file. Usare GNU Diffutils 3.7 confrontando due file con le stesse dimensioni del file ma contenuto diverso con diff -q file1 file2output Files file1 and file2 differ.
Stefan Schmidt,

50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Questo comando ti darà i file che sono in dir1 e non in dir2.

A proposito di <( )segno, puoi cercarlo come "sostituzione del processo".


andrebbe bene lavorare anche con le sottodirectory, penso che (ls -R dir1|sort)potrebbe fare il trucco
Ulkas,

1
Funzionerebbe con la modalità di ripristino di OS X.
Anthony Vanover,

@ulkas, l'output potrebbe essere errato se si utilizza (ls -R dir|sort).
Andriy Makukha,

3
vimdiff offre un confronto visivo molto più gradevole con l'evidenziazione dei colori:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

Un buon modo per fare questo confronto è usare findcon md5sum, quindi a diff.

Esempio:

Utilizzare findper elencare tutti i file nella directory, quindi calcolare l'hash md5 per ciascun file e reindirizzarlo a un file:

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

Esegui la stessa procedura in un'altra directory:

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

Quindi confrontare il risultato due file con "diff":

diff dir1.txt dir2.txt

Questa strategia è molto utile quando le due directory da confrontare non sono nella stessa macchina ed è necessario assicurarsi che i file siano uguali in entrambe le directory.

Un altro buon modo per fare il lavoro è usare git

git diff --no-index dir1/ dir2/

I migliori saluti!


1
Non sono andato git potrebbe fare una differenza su directory arbitrarie che non sono all'interno di un repository git ... fantastico !!! Questa risposta mi ha appena risolto un grosso problema, grazie
ViktorNova,

17

Meld ( http://meldmerge.org/ ) fa un ottimo lavoro nel confrontare directory e file all'interno.

Combina directory di confronto


Tranne che la fusione fa un pessimo lavoro quando si tratta di terminazioni di linea ...
0xC0000022L

1
Non ho mai avuto problemi con le terminazioni di linea. Puoi dettagli?
Catalin Hritcu,

Sì, non indica le terminazioni di riga. Ciò ha (ripetutamente) indotto gli sviluppatori a utilizzare questo strumento impegnando modifiche che "riparavano" le terminazioni di linea trasformando un CRLF in CRLFLF, ad esempio.
0xC0000022L

3
Insiste anche sulla lettura del contenuto dei file ed è quindi quasi inutile con le directory >> 1GB.
Tomislav Nakic-Alfirevic,

13

Il plugin DirDiff di vim è un altro strumento molto utile per confrontare le directory.

vim -c "DirDiff dir1 dir2"

Non solo elenca quali file sono diversi tra le directory, ma consente anche di ispezionare / modificare con vimdiff i file che sono diversi.


11

Insoddisfatto di tutte le risposte, poiché la maggior parte di esse lavora molto lentamente e produce output inutilmente lunghi per directory di grandi dimensioni, ho scritto il mio script Python per confrontare due cartelle.

A differenza di molte altre soluzioni, non confronta i contenuti dei file. Inoltre non va all'interno delle sottodirectory mancanti in un'altra directory. Quindi l'output è piuttosto conciso e lo script funziona velocemente.

#!/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])

Esempio di utilizzo:

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

O se vuoi vedere solo i file dalla prima directory:

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 Se devi confrontare le dimensioni dei file e gli hash dei file per potenziali modifiche, ho pubblicato uno script aggiornato qui: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


Script abbastanza semplice che fa esattamente quello che volevo: verifica una copia in blocco: +1 da me. (è necessario convertirlo in python2 però) Suggerimento: l'uso dei set potrebbe semplificare la parte diff.
Jason Morgan,

6

Un altro approccio (forse più veloce per directory di grandi dimensioni):

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

Il sedcomando rimuove il primo componente della directory grazie al post di Erik )


1
Credo che questo metodo sia più semplice (usando ancora findun commento e non una risposta separata): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null questo stamperà i file presenti in dir2 ma non presenti in dir1.
Alexander Amelkin,

5

È un po 'tardi ma può aiutare qualcuno. Non sono sicuro che diff o rsync sputino solo i nomi dei file in un formato semplice come questo. Grazie a plhn per aver dato quella bella soluzione che ho ampliato di seguito.

Se vuoi solo i nomi dei file, quindi è facile copiare i file che ti servono in un formato pulito, puoi usare il comando find.

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

Ciò presuppone che sia dir1 che dir2 siano nella stessa cartella principale. sed rimuove semplicemente la cartella principale in modo da poter confrontare le mele con le mele. L'ultimo sed riporta semplicemente il nome dir1.

Se vuoi solo file:

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

Allo stesso modo per le directory:

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

1
Si noti che si potrebbe fare una cdprima della findinvece di dover utilizzare sed, ad esempio: comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). (I messaggi exitqui per impedire l' findutilizzo della directory corrente dovrebbero cdfallire.)
phk

Si noti inoltre che la soluzione potrebbe non riuscire quando sono presenti file con determinati caratteri speciali, se si dispone di una versione molto recente di commcon supporti -z(fornita con git.savannah.gnu.org/cgit/coreutils.git/commit/… ) è possibile eseguire comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (Nel frattempo ho anche capito che gli exits potevano essere sostituiti.)
phk,

5

La risposta accettata elencherà anche i file esistenti in entrambe le directory, ma con contenuto diverso. Per elencare SOLO i file esistenti in dir1 è possibile utilizzare:

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

Spiegazione:

  • diff -r dir1 dir2: confronta
  • grep "Solo in": ottieni righe che contengono "Solo in"
  • grep dir1: ottiene le righe che contengono dir

5

Questa risposta ottimizza uno dei suggerimenti di @ Adail-Junior aggiungendo l' -Dopzione, che è utile quando nessuna delle directory confrontate è repository git:

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

Se lo usi -D, non vedrai confronti con /dev/null: text Binary files a/whatever and /dev/null differ


È stato molto utile nel confrontare due directory, si vedono immediatamente le differenze tra i file. Ovviamente funziona meglio su file con contenuto testuale.
Erich Kuester,

1

Un modo semplificato per confrontare 2 directory usando il comando DIFF

diff nomefile.1 nomefile.2> nomefile.dat >> Invio

aprire nomefile.dat al termine dell'esecuzione

e vedrai: Solo in nomefile.1: nomefile.2 Solo in: nome_directory: nome_di_file1 Solo in: nome_directory: nome_di_file2


Perché devi eseguire l'output in un file .dat?
Vishnu NK,

1

Questo è lo script bash per stampare i comandi per la sincronizzazione di due directory

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|" 

0

GNU greppuò invertire la ricerca con l'opzione -v. Questo rende la grepsegnalazione delle righe che non corrispondono. In questo modo è possibile rimuovere i file dir2dall'elenco dei file in dir1.

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

Le opzioni -F -xindicano grepdi eseguire una ricerca di stringhe sull'intera riga.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.