narzędzia linux diff: utwórz listę zmodyfikowanych plików

14

Jak utworzyć listę zmodyfikowanych plików programowo przy użyciu narzędzi wiersza poleceń systemu Linux? Nie interesuje mnie różnica w żadnym konkretnym pliku (delta, łatka). Chcę tylko mieć listę nowych lub zmodyfikowanych plików w porównaniu do poprzedniej wersji produktu. Aby móc opublikować nową aktualizację produktu.

aktualizacja: diff -qrnie daje bardzo wygodnych wyników. Dane wyjściowe diff -qrrównież muszą zostać przetworzone. Czy jest jakiś lepszy sposób?

Alfa Syzyf
źródło
jaki jest przykład „wygodnego” wyjścia?
frogstarr78

Odpowiedzi:

8

Mam do tego proste podejście: użyj trybu podglądu rsync:

rsync -aHSvn --delete old_dir/ new-dir/

Pliki oznaczone jako „do usunięcia” za pomocą tego polecenia będą plikami „nowymi”. Pozostałe, które mają zostać przeniesione, zmieniły się w jakiś sposób. Więcej informacji znajduje się na stronie rsync-man-page.

Nils
źródło
13

Można użyć diff toool: patrz opcje -q oraz -r

-q  --brief
Output only whether files differ.

-r  --recursive
Recursively compare any subdirectories found.

Przykład:

diff -qr dir1 dir2
stivlo
źródło
Absolutnie okropne i nieczytelne dane wyjściowe, zaśmiecone nonsensownymi informacjami, Only inktóre pojawiają się, nawet jeśli katalogi są idealnymi kopiami. Musiałem porównać zmiany ze starą wersją i ostatecznie pobrać całą wersję do osobnego katalogu i użyć standardowych narzędzi SVN do porównania. To chyba jedyna droga…
Hi-Angel,
3

diffutilsPakiet zawiera lsdiffnarzędzia. Wystarczy przekazać dane wyjściowe diff -udo lsdiff:

diff -u --other-diff-options path1 path2 | lsdiff
ninjalj
źródło
Dobra sugestia, dziękuję. Był w patchutilspakiecie dla mnie (CentOS 5.x).
Steve Kehlet
Tak, również pakiet patchutils dla Ubuntu / Debian.
artfulrobot
1

Chciałbym tylko dotknąć pliku w momencie każdej aktualizacji, a następnie można znaleźć pliki, które zostały zmodyfikowane od tego czasu za pomocą find /tree/location -newer /last/update/file -print

Paul Tomblin
źródło
1

Aby wziąć tylko nazwę plików, które zmienili, używam tego polecenia:

diff -r dirt1 dir2 --brief | sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/'

Jeśli chcesz wykluczyć niektóre pliki jako pliki obiektów lub pliki bibliotek, możesz użyć:

diff -r dirt1 dir2 --brief --exclude "*.o" --exclude "*.a" | sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/'
Felicià Barceló
źródło
1

Aby utworzyć listę nowych lub zmodyfikowanych plików programowo najlepszym rozwiązaniem, jakie mogłem wymyślić, jest użycie rsync , sort i uniq :

(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq

Pozwól mi wyjaśnić za pomocą tego przykładu: chcemy porównać dwie wersje dokuwiki, aby zobaczyć, które pliki zostały zmienione, a które nowo utworzone.

Ściągamy smoły za pomocą wget i wyodrębniamy je do katalogów old/oraz new/:

wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29d.tgz
wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29.tgz
mkdir old && tar xzf dokuwiki-2014-09-29.tgz -C old --strip-components=1
mkdir new && tar xzf dokuwiki-2014-09-29d.tgz -C new --strip-components=1

Uruchomienie rsync w jedną stronę może spowodować pominięcie nowo utworzonych plików, ponieważ porównanie rsync i diff pokazuje tutaj:

rsync -rcn --out-format="%n" old/ new/

daje następujące dane wyjściowe:

VERSION
doku.php
conf/mime.conf
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php

Uruchomienie rsync tylko w jednym kierunku powoduje pominięcie nowo utworzonych plików, a na odwrót pominięcie usuniętych plików, porównanie danych wyjściowych diff:

diff -qr old/ new/

daje następujące dane wyjściowe:

Files old/VERSION and new/VERSION differ
Files old/conf/mime.conf and new/conf/mime.conf differ
Only in new/data/pages: playground
Files old/doku.php and new/doku.php differ
Files old/inc/auth.php and new/inc/auth.php differ
Files old/inc/lang/no/lang.php and new/inc/lang/no/lang.php differ
Files old/lib/plugins/acl/remote.php and new/lib/plugins/acl/remote.php differ
Files old/lib/plugins/authplain/auth.php and new/lib/plugins/authplain/auth.php differ
Files old/lib/plugins/usermanager/admin.php and new/lib/plugins/usermanager/admin.php differ

Uruchomienie rsync na dwa sposoby i sortowanie danych wyjściowych w celu usunięcia duplikatów ujawnia, że ​​katalog data/pages/playground/i plik data/pages/playground/playground.txtzostały początkowo pominięte:

(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq

daje następujące dane wyjściowe:

VERSION
conf/mime.conf
data/pages/playground/
data/pages/playground/playground.txt
doku.php
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php

rsync jest uruchamiany z tymi argumentami:

  • -r „przekierowywać do katalogów”,
  • -c aby również porównać pliki o identycznym rozmiarze i tylko „pomiń na podstawie sumy kontrolnej, a nie czasu i rozmiaru mod”,
  • -n „wykonać jazdę próbną bez zmian”, oraz
  • --out-format="%n" do „wypisywania aktualizacji za pomocą określonego FORMATU”, czyli „% n” tutaj tylko dla nazwy pliku

Dane wyjściowe (lista plików) rsyncw obu kierunkach są łączone i sortowane za pomocą sort, a ta posortowana lista jest następnie zagęszczana poprzez usunięcie wszystkich duplikatów za pomocąuniq

iolsmit
źródło
0

Powinieneś uzyskać pożądany efekt za pomocą:

diff -r --brief dir1/ dir2/
Marcin Gil
źródło
0

To może załatwić sprawę:

compare_dirs()
{
    # Shows which files and directories exist in one directory but not both
    if [ $# -ne 2 ]
    then
        echo "Usage: compare_dirs dir1 dir2" >&2
        return 2
    fi
    for path
    do
        if [ ! -d "$path" ]
        then
            echo "Not a directory: $path" >&2
            return 1
        fi
    done
    comm -3 \
        <(cd -- "$1" && find . -printf '%P\0' | sort -z | quote_shell) \
        <(cd -- "$2" && find . -printf '%P\0' | sort -z | quote_shell)
}
l0b0
źródło
0

Zwykle umieszczasz pliki w jakimś systemie kontroli wersji, takim jak SubVersion lub git, ponieważ mogą to zrobić po wyjęciu z pudełka.

Ale możesz zrobić szybki skrypt z pętlą for na dir1, a następnie porównać każdy plik z plikiem w dir2. Pętla for może spojrzeć na kod wyjścia z diff, aby dowiedzieć się, czy pliki były inne.

Może coś takiego:

for f in `(cd dir1 ; find .)`
do 
  diff $f ../dir2/$f
  if [ "$?" == "0" ]
  then 
    echo same
  else 
    echo diff: $f
  fi
done

Uwaga: Skrypt nie jest testowany, więc powyższy przykład to „pseudokod inspirowany bash” ...


Spróbujmy jeszcze raz, ale z git

Utwórz przykładowe pliki do gry

mkdir -p dir1/test1/test11
mkdir -p dir1/test1/test12
mkdir -p dir1/test1/test13
echo "Test1" >> dir1/test1/test11/t1.txt
echo "Test2" >> dir1/test1/test12/t2.txt
echo "Test3" >> dir1/test1/test13/t3.txt

#And a dir to work in
mkdir gitdir

Następnie wpisz katalog i zaimportuj katalog1

cd gitdir/
git init .
cp -r ../dir1/* .
git add .
git commit -m 'dir1'

Wyjdź i zmodyfikuj katalog 1 (aby stał się katalogiem 2)

cd ..
echo "Test2" > dir1/test1/test11/t1.txt

Następnie przejdź do katalogu git i zaimportuj nowy katalog

cd gitdir/
cp -r ../dir1/* .

Teraz zapytaj gita, co się zmieniło (za pomocą polecenia status)

git status -s

Dane wyjściowe to lista zmian, która wygląda następująco:

 M test1/test11/t1.txt
Johan
źródło
0

Może byłbyś szczęśliwszy z czegoś innego. Spróbować git.

Zrób to jako przykład:

mkdir a
cd a
git init
touch b
git add . && git commit -m "Empty file"
git status
echo c >> b
git status
git add . && git commit -m "Full file"
git status

gitśledzi twoje pliki za ciebie. Polecenie git statuspokaże wszystkie pliki, które zostały zmodyfikowane od ostatniego zatwierdzenia.

bahamat
źródło
0

Jest to podobne do rsync: pokazuje, kiedy nowszy plik docelowy ma zostać zastąpiony (zapytany później, ale nie duplikat).

Jak wskazano w pytaniu, „diff -q -r” może wymagać pewnego przetwarzania, aby było przydatne. Pytanie nie określiło formy wyniku; odpowiedzi dają różne rodzaje raportów.

rsyncjest przydatnym narzędziem do tego celu, ponieważ jest znacznie szybszy niż diff. Jednak rozwiązanie sugerowane przez @nils jest znacznie bardziej szczegółowe (i wyświetla więcej plików) niż faktyczne różnice między starymi / nowymi drzewami katalogów. Na przykład porównując to ze skryptem, który napisałem dla tej odpowiedzi i działając na tych samych danych,

  • Odpowiedź @ nils tworzy 605 linii (najwyraźniej dlatego, że zawiera zmiany w katalogu ),
  • „diff -q -r” tworzy 352 wiersze po uruchomieniu przez kilka minut, oraz
  • mój skrypt pokazuje 252 wiersze ( rzeczywiste pliki zmienione, dodane lub usunięte)

Aby diffpoprawnie uwzględnić nowe pliki, potrzebujesz również -Nopcji (której nie widzę w żadnej z sugerowanych odpowiedzi). Ponieważ jednak jest on znacznie wolniejszy (rzędy wielkości) niż rsyncpoprawa wydajności tego drugiego wydaje się być dobrym rozwiązaniem.

Dalsza lektura

Thomas Dickey
źródło
0

Zawsze byłem stronniczy w stosunku do sha1sum (lub nawet md5sum; w tym kontekście jest to całkiem bezpieczne).

find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/before
# don't miss the "sort" in there; it's important

# (later)
find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/after
vimdiff /tmp/before /tmp/after
# or whatever diff tool you like, even "diff -u"

Czasami - tak jak w przypadku zmiany nazwy lub przenoszenia wielu plików - sortowanie w pierwszym polu, a następnie wykonanie różnicy może pomóc, ale w większości przypadków jest to wystarczające.

Zauważ, że w porównaniu do niektórych innych metod ma to tę zaletę, że nie musisz przechowywać kopii plików „przed”; tylko plik wyjściowy md5sum.

sitaram
źródło