carpetas de combinación de Linux: rsync?

13

Tengo dos copias de una carpeta

src/
dest/

Quiero fusionarlos, haciendo lo siguiente:

Si solo hay un archivo src, quiero que se mueva adest

Si solo hay un archivo dest, quiero que se ignore IE solo.

Si un archivo está en ambos y tiene contenido idéntico (es decir, el mismo tamaño y fecha), elimine desrc

Si un archivo está en ambos y no tiene contenido idéntico, déjelo atrás srcpara que pueda combinarlos manualmente.

Solo un número muy pequeño de archivos (entre 0% y 5% del total de archivos) debe estar en esta última categoría, pero no sé cómo separar el archivo en ambos y el mismo de ambos, pero diferente.

He intentado descubrir cómo hacer esto, rsyncpero fue en vano hasta ahora.

David Oneill
fuente

Respuestas:

17

Solo he realizado pruebas de funcionalidad limitadas, así que tenga cuidado con este comando (--dry-run):

rsync -avPr --ignore-existing --remove-source-files src/ dest

Tenga en cuenta el final / ya que esto se repetirá en src en lugar de copiar src en sí, esto debería mantener sus rutas existentes.

Al utilizar el indicador --ignore-existente en combinación con el indicador --remove-source-files, eliminará solo los archivos de src que se sincronizan de src a dest, es decir, archivos que no existían anteriormente solo en dest.

Para eliminar archivos no sincronizados, es decir, aquellos que ya existían en dest / as en src /, puede usar:

for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done

o

find src -type f -exec bash -c 'cmp -s "$0" "${0/#src/dest}" && rm "$0"' {} \;

si los nombres de los archivos pudieran contener espacios en blanco / nuevas líneas / ... Con respecto al comentario de Gilles sobre caracteres especiales, eso es ciertamente algo a tener en cuenta y hay muchas soluciones, la más simple sería pasar un -i a rm que se solicitará antes de toda eliminación. Siempre que se proporcione src /, o su ruta principal, para encontrar, sin embargo, la ruta totalmente calificada debería hacer que todos los nombres de archivo sean manejados correctamente por los comandos diff y rm sin citar.

Tok
fuente
corrección: ese comando no eliminará archivos de src si ya existe una copia idéntica en dest
Tok
Sí :(. Esa es la parte que me cuesta entender.
David Oneill
2
Bueno, la buena noticia es que puede resolverlo de forma independiente sin mucha molestia: for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done(puede omitirlo || echo $filesi lo desea, está incluido para completar)
Tok
Nifty: eso es lo que necesitaba. Edita eso en tu respuesta, ¡y lo aceptaré!
David Oneill
@Tok: su comando se ahogará con los nombres de archivo que contienen caracteres especiales (espacios en blanco \?*[, iniciales -). Debe usar comillas dobles alrededor de sustituciones variables , pasar --a utilidades antes de los nombres de archivo, usar en find … -exec …lugar de analizar la salida de find. Con un rmcomando en la mezcla, esta es una receta para el desastre.
Gilles 'SO- deja de ser malvado'
6

Unison es la herramienta que estás buscando. Prueba unison-gtk si prefieres una interfaz gráfica de usuario. Pero no creo que elimine archivos similares: intente al unísono que ambos directorios sean idénticos. Sin embargo, fácilmente 1) identificará qué archivos se copiarán; 2) cuáles necesitan fusión manual.

simonp
fuente
No hace exactamente lo que pide el OP, pero parece que logra el objetivo final del OP. +1
Ryan C. Thompson
+1 Lamentablemente, el servidor en el que estoy ejecutando esto no tiene instalado al unísono, ni tengo los permisos para instalarlo. Pero esta podría ser una buena respuesta para otra persona.
David Oneill
1
Puede descargar el ejecutable al unísono desde seas.upenn.edu/~bcpierce/unison//download/… . Instálelo en algún lugar de su directorio de inicio, es solo un archivo.
JooMing
2

El siguiente script debería hacer las cosas razonablemente. Mueve los archivos desde el origen al destino, nunca sobrescribe un archivo y crea directorios según sea necesario. Los archivos de origen que tienen un archivo diferente correspondiente en el destino se dejan solos, al igual que los archivos que no son archivos o directorios normales (por ejemplo, enlaces simbólicos). Los archivos que quedan en la fuente son aquellos por los cuales existe un conflicto. Cuidado, no lo he probado en absoluto.

cd src
find . -exec sh -c '
    set -- "/path/to/dest/$0"
    if [ -d "$0" ]; then #  the source is a directory 
      if ! [ -e "$1" ]; then
        mv -- "$0" "$1"  # move whole directory in one go
      fi
    elif ! [ -e "$0" ]; then  # the source doesn't exist after all
      :  # might happen if a whole directory was moved
    elif ! [ -e "$1" ]; then  # the destination doesn't exist
      mv -- "$0" "$1"
    elif [ -f "$1" ] && cmp -s -- "$0" "$1"; then  # identical files
      rm -- "$0"
    fi
  ' {} \;

Otro enfoque sería hacer una unión montando un directorio sobre el otro, por ejemplo con funionfs o unionfs-fuse .

Gilles 'SO- deja de ser malvado'
fuente