
Tengo un rsync
proceso que sincroniza contenido de un repositorio de origen (que está controlado por versión) en un montaje NFS compartido.
El escenario (por más terrible que sea) es que la carpeta de destino contiene más contenido que la carpeta de origen porque otro contenido se sincroniza con la carpeta de destino desde diferentes fuentes. Así, por ejemplo, las estructuras de carpetas pueden verse así:
fuente
a/a1.txt
a/a2.txt
b/b1.txt
destino
a/a1.txt
a/a2.txt
a/a3.txt
b/b1.txt
c/c1.txt
(en este ejemplo, a/a3.txt
y c/c1.txt
están sincronizados con el destino desde otro lugar. En la práctica, esto involucra muchas otras fuentes y no se puede influir en el contenido/procesos de estas).
Ahora digamos que la carpeta de origen elimina el a/a2.txt
archivo. Con la configuración existente, este archivo no se eliminará en el destino; pero usarlo --delete
resultaría en la eliminación de otros archivos, y es un requisito no hacerlo.
¿Cómo podría --delete
usarse en este rsync pero cumplir con el requisito? Debido a que la versión del directorio fuente está controlada, es bastante sencillo obtener un antes y un después de este directorio, por lo que se podría calcular una copia de seguridad diferencial utilizando el directorio fuente original como referencia, pero ¿es esta la mejor manera?
Respuesta1
No puedes usarlo rsync --delete
así. No tiene estado y no guarda ningún registro de qué archivos se han eliminado entre ejecuciones. La --delete
bandera simplemente indica rsync
que se eliminen todos los archivos del destino que no existan en el origen.
Para implementar esta eliminación restringida, creo que es necesario mantener su propio estado. Ni rsync
ni unison
puedo hacer esto por ti.
La siguiente no es una solución totalmente segura contra errores; es un punto de partida. (Sin embargo, maneja archivos con nombres extraños, incluidos aquellos que contienen una nueva línea incrustada).
Supongamos dos directorios src
y dst
. (Para los propósitos del ejemplo, realmente no importa si dst
es local o remoto).
# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z > .state.src
Cada vez que realicemos una copia de seguridad, ejecute el siguiente código
# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/
# Determine the set of files to delete in "dst/"
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z | tee .state.src.new |
LC_ALL=C comm -z - -13 .state.src |
ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'
# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src
Si su versión comm
(como la mía) es anterior a GNU coreutils 8.25 y no tiene la -z
bandera, puede usar esta solución alternativa:
# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort > .state.src
Cada vez que realicemos una copia de seguridad, ejecute el siguiente código
# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/
# Determine the set of files to delete in "dst/"
( cd src && find . -type f -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort | tee .state.src.new |
LC_ALL=C comm -13 - .state.src |
tr '\0\n' '\n\0' |
ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'
# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src