herramientas de linux diff: crea una lista de archivos modificados


14

¿Cómo creo una lista de archivos modificados mediante programación usando las herramientas de línea de comandos de Linux? No me interesa la diferencia en ningún archivo en particular (delta, parche). Solo quiero tener una lista de archivos nuevos o modificados en comparación con la versión anterior del producto. Para poder publicar una nueva actualización del producto.

actualización: diff -qrno produce resultados muy convenientes. La salida de diff -qrtambién necesita ser procesada. ¿Hay alguna forma mejor?


¿Cuál es un ejemplo de salida "conveniente"?
frogstarr78

Respuestas:


8

Tengo un enfoque simple para esto: use el modo de vista previa rsync:

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

Los archivos que se muestran como "para ser eliminados" por ese comando serán los archivos "nuevos". Los otros que serán transferidos han cambiado de alguna manera. Consulte la página de manual de rsync para obtener más detalles.


13

Puede usar el diff toool: vea las opciones -q y -r

-q  --brief
Output only whether files differ.

-r  --recursive
Recursively compare any subdirectories found.

Ejemplo:

diff -qr dir1 dir2

Salida absolutamente horrible e ilegible, repleta de información sin sentido que dice Only inlo que aparece incluso si los directorios son copias ideales. Tenía que comparar los cambios con una revisión anterior y terminar descargando la revisión completa en un directorio separado y usar herramientas SVN estándar para comparar. Ese parece ser el único camino a seguir ...
Hola-Angel

3

El diffutilspaquete incluye una lsdiffherramienta. Simplemente pase la salida de diff -ua lsdiff:

diff -u --other-diff-options path1 path2 | lsdiff

Buena sugerencia, gracias. Estaba en el patchutilspaquete para mí (CentOS 5.x).
Steve Kehlet

Sí, el paquete patchutils también para Ubuntu / Debian.
artfulrobot

1

Simplemente tocaría un archivo en el momento de cada actualización, y luego puede encontrar archivos que se modificaron desde entonces con find /tree/location -newer /last/update/file -print


1

Para tomar solo el nombre de los archivos que cambiaron, utilizo este comando:

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

Si necesita excluir algunos archivos como archivos de objetos o archivos de biblioteca, puede usar:

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

1

Para crear una lista de archivos nuevos o modificados mediante programación, la mejor solución que se me ocurre es usar rsync , sort y uniq :

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

Permítanme explicar con este ejemplo: queremos comparar dos versiones de dokuwiki para ver qué archivos se cambiaron y cuáles se crearon recientemente.

Buscamos los tars con wget y los extraemos en los directorios old/y 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

Ejecutar rsync de una manera puede perder archivos recién creados como se muestra aquí en la comparación de rsync y diff:

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

produce el siguiente resultado:

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

Al ejecutar rsync solo en una dirección, se pierden los archivos recién creados y, al revés, se pierden los archivos eliminados, compare la salida de diff:

diff -qr old/ new/

produce el siguiente resultado:

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

Ejecutar rsync en ambos sentidos y ordenar la salida para eliminar duplicados revela que el directorio data/pages/playground/y el archivo data/pages/playground/playground.txtse perdieron inicialmente:

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

produce el siguiente resultado:

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 se ejecuta con estos argumentos:

  • -r "recurrir a directorios",
  • -c para comparar también archivos de tamaño idéntico y solo "omitir en función de la suma de comprobación, no de mod-time & size",
  • -n para "realizar una ejecución de prueba sin realizar cambios", y
  • --out-format="%n" "generar actualizaciones utilizando el FORMATO especificado", que es "% n" aquí solo para el nombre del archivo

La salida (lista de archivos) de rsyncen ambas direcciones se combina y ordena usando sort, y esta lista ordenada se condensa eliminando todos los duplicados conuniq


0

Debería obtener el resultado deseado usando:

diff -r --brief dir1/ dir2/

0

Esto podría hacer el truco:

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)
}

0

Normalmente coloca los archivos en algún tipo de sistema de control de versiones como SubVersion o git, ya que pueden hacer esto por usted de inmediato.

Pero podría hacer un script rápido con un bucle for en dir1 y luego comparar cada archivo con el de dir2. El bucle for puede mirar el código de salida de diff para saber si los archivos eran diferentes.

Tal vez algo como esto:

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

Nota: El script no se prueba, por lo que el ejemplo anterior es "pseudocódigo inspirado en bash" ...


Vayamos otra vez pero con git

Crea algunos archivos de ejemplo para jugar

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

Luego ingrese el directorio e importe dir1

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

Salga y modifique dir1 (para que se convierta en su dir2)

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

Luego vaya al directorio git e importe el nuevo directorio

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

Ahora pregúntale a git qué ha cambiado (con el comando de estado)

git status -s

El resultado es una lista con los cambios, que se ve así:

 M test1/test11/t1.txt

0

Tal vez estarías más feliz con algo diferente. Tratar git.

Haz esto como un ejemplo:

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

gitrastreará sus archivos por usted. El comando git statusle mostrará todos los archivos que se han modificado desde la última confirmación.


0

Esto es similar a rsync: muestra cuándo se sobrescribirá el archivo más nuevo en el destino (se solicita más adelante, aunque no es un duplicado).

Como se indica en la pregunta, "diff -q -r" puede requerir algún procesamiento para ser útil. La pregunta no especificaba la forma de la salida; Las respuestas dan diferentes tipos de informes.

rsynces una herramienta útil para este propósito porque es mucho más rápido que diff. Sin embargo, la solución sugerida por @nils es mucho más detallada (y enumera más archivos) que las diferencias reales entre los árboles de directorio antiguos / nuevos. Por ejemplo, comparando eso con el script que escribí para esa respuesta y ejecutándome con los mismos datos,

  • La respuesta @nils produce 605 líneas (aparentemente porque incluye cambios de directorio ),
  • "diff -q -r" produce 352 líneas después de ejecutarse durante varios minutos, y
  • mi script muestra 252 líneas ( los archivos reales han cambiado, agregado o eliminado)

Para dar diffcuenta adecuada de los nuevos archivos, también necesita la -Nopción (que no veo en ninguna de las respuestas sugeridas). Sin embargo, dado que es mucho más lento (órdenes de magnitud) que rsync, mejorar el rendimiento de este último parece el camino a seguir.

Otras lecturas


0

Siempre he sido parcial a sha1sum (o incluso md5sum; en este contexto es bastante seguro).

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"

A veces, como si se cambian los nombres o se mueven demasiados archivos, ordenar en el primer campo y luego hacer la diferencia podría ayudar, pero la mayoría de las veces esto es lo suficientemente bueno.

Tenga en cuenta que, en comparación con algunos de los otros métodos, esto tiene la ventaja de que no necesita guardar una copia de los archivos "anteriores"; solo el archivo de salida md5sum.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.