Copias de seguridad

rclone bisync: conflictos, borrados y el límite de seguridad en una prueba real

Publicado el 29 de octubre de 2026Última revisión: 29 de octubre de 20267 min de lectura

Resultado de la prueba

sin --resync previo bisync dio exit 2. Con un fichero editado en los dos lados no se perdió ninguno: quedaron f1.txt..path1 y f1.txt..path2 en ambas carpetas. Un borrado se propagó (exit 0). Borrar 4 de 12 con --max-delete 20 abortó con exit 1 y 5 de 8 con el límite por defecto (50) también; con --force se propagó. Una carpeta vacía abortó con exit 2 incluso con --force.

Contenido

Este artículo no contiene enlaces de afiliado.

rclone bisync mantiene dos ubicaciones iguales en las dos direcciones, a diferencia de sync, que solo va de origen a destino. Eso lo hace útil y peligroso: un borrado en un lado se propaga al otro. Se probó con dos carpetas locales qué hace en los casos que de verdad importan.

Condiciones: rclone 1.60.1 (paquete de Ubuntu 24.04) en Linux arm64, dos carpetas locales (a es Path1 y b es Path2), diez ficheros de texto pequeños. rclone avisa de que bisync es EXPERIMENTAL en esa versión. No se probó con almacenamiento remoto (Drive, S3...), con carpetas, con ficheros grandes, con --conflict-resolve ni con versiones más recientes de rclone, donde el comportamiento puede ser distinto. Script completo al final.

Primer uso: --resync

Sin --resync, la primera ejecución falla:

ERROR : Bisync critical error: cannot find prior Path1 or Path2 listings, likely due to critical error on prior run
ERROR : Bisync aborted. Must run --resync to recover.

Exit 2. Con --resync copió los 10 ficheros de a a b y terminó con "Bisync successful" (exit 0). Una ejecución posterior sin cambios dio exit 0. Con ficheros nuevos en cada lado (nuevo-a.txt y nuevo-b.txt), cada uno se copió al otro y ambas carpetas quedaron con 12 ficheros.

Conflicto: el mismo fichero editado en los dos lados

Se escribió f1.txt con contenidos distintos en a y b. Resultado (exit 0):

Carpeta Ficheros resultantes
a f1.txt..path1 (editado en a), f1.txt..path2 (editado en b)
b los mismos dos

No se perdió ninguna versión, pero tampoco existe ya f1.txt: rclone renombró las dos copias y las dejó en ambos lados. Hay que decidir a mano cuál conservar. La propia salida dijo Renamed: 2. Si tu programa espera encontrar f1.txt, no lo encontrará.

Borrado: se propaga

Al borrar f2.txt en a, bisync lo borró de b (Deleted: 1 (files), exit 0). No se probó si queda copia de lo borrado (--backup-dir).

Caso de borrado contra edición: se borró f3.txt en b y se editó en a. Ganó la edición: f3.txt se copió de nuevo a b con el contenido editado, sin error (exit 0).

--max-delete y las protecciones

En esta versión --max-delete es un porcentaje (por defecto 50), no un número de ficheros.

Situación Resultado
a vacío por completo (12 ficheros en b) Empty current Path1 listing. Cannot sync to an empty directory, exit 2; b sigue con 12
4 de 12 borrados en a con --max-delete 20 Safety abort: too many deletes (>20%, 4 of 12), exit 1; b sigue con 12
mismo reintento sin --force aborta igual, exit 1
los mismos 4 borrados con el límite por defecto Deleted: 4, exit 0; a 8, b 8
5 de 8 borrados en a, límite por defecto Safety abort: too many deletes (>50%, 5 of 8), exit 1; b sigue con 8
los mismos 5 con --force Deleted: 5, exit 0; a 3, b 3
a vacío con --force sigue abortando, exit 2; b sigue con 3

Detalles que importan

Script completo

#!/bin/bash
# rclone bisync between two local dirs: --resync, edit conflict, deletion propagation, --max-delete. Usage: test-rclone-bisync.sh WORKDIR
W=$1; rm -rf "$W"; mkdir -p "$W/a" "$W/b"
rclone version | head -1
A="$W/a"; B="$W/b"
for i in 1 2 3 4 5 6 7 8 9 10; do echo "contenido $i" > "$A/f$i.txt"; done
echo "== bisync sin --resync en el primer uso"
rclone bisync "$A" "$B" 2>&1 | tail -3; echo "exit=${PIPESTATUS[0]}"
echo "== primer --resync"
rclone bisync "$A" "$B" --resync -v 2>&1 | grep -E "Copied|Bisync successful|ERROR|Resync"; echo "exit=${PIPESTATUS[0]}"
ls "$B" | wc -l | sed 's/^/ficheros en b: /'
echo "== bisync sin cambios"
rclone bisync "$A" "$B" 2>&1 | tail -2; echo "exit=${PIPESTATUS[0]}"
echo "== fichero nuevo en cada lado"
echo "solo en a" > "$A/nuevo-a.txt"; echo "solo en b" > "$B/nuevo-b.txt"
rclone bisync "$A" "$B" -v 2>&1 | grep -E "Copied|Bisync successful|ERROR"; echo "exit=${PIPESTATUS[0]}"
ls "$A" | wc -l | sed 's/^/ficheros en a: /'; ls "$B" | wc -l | sed 's/^/ficheros en b: /'
echo "== conflicto: f1.txt editado en los dos lados"
echo "editado en a" > "$A/f1.txt"; sleep 1; echo "editado en b" > "$B/f1.txt"
rclone bisync "$A" "$B" -v 2>&1 | grep -iE "conflict|Copied|Renamed|Bisync successful|ERROR"; echo "exit=${PIPESTATUS[0]}"
for d in a b; do echo "-- $d:"; for f in "$W/$d"/f1*; do echo "$(basename $f): $(cat $f)"; done; done
echo "== borrado: f2.txt se borra en a"
rm "$A/f2.txt"
rclone bisync "$A" "$B" -v 2>&1 | grep -E "Deleted|Bisync successful|ERROR"; echo "exit=${PIPESTATUS[0]}"
ls "$B" | grep -c '^f2.txt$' | sed 's/^/f2.txt en b: /'
echo "== borrado en b de f3.txt, y en a se edita f3.txt (borrado contra edición)"
rm "$B/f3.txt"; echo "f3 editado en a" > "$A/f3.txt"
rclone bisync "$A" "$B" -v 2>&1 | grep -E "Copied|Deleted|Bisync successful|ERROR"; echo "exit=${PIPESTATUS[0]}"
echo "f3 en a: $(cat $A/f3.txt 2>&1)"; echo "f3 en b: $(cat $B/f3.txt 2>&1)"
echo "== --max-delete: se vacía a (borrado accidental del directorio origen)"
n=$(ls "$B" | wc -l); echo "ficheros en b antes: $n"
rm -f "$A"/*
rclone bisync "$A" "$B" -v 2>&1 | grep -E "Deleted|Bisync successful|ERROR|max-delete|Safety|abort" | head -5; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== reintento inmediato (sin --resync)"
rclone bisync "$A" "$B" 2>&1 | grep -E "Bisync successful|ERROR|Bisync critical|resync" | head -4; echo "exit=${PIPESTATUS[0]}"
echo "== recuperación: se restauran los ficheros en a desde b y se hace --resync"
rclone copy "$B" "$A"
rclone bisync "$A" "$B" --resync 2>&1 | tail -1; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== --max-delete 20 con 4 borrados de $(ls $A | wc -l)"
ls "$A" | head -4 | while read f; do rm "$A/$f"; done
rclone bisync "$A" "$B" --max-delete 20 -v 2>&1 | grep -E "Deleted|Bisync successful|ERROR" | head -6; echo "exit=${PIPESTATUS[0]}"
echo "== reintento tras el aborto de seguridad (sin --force ni --resync)"
rclone bisync "$A" "$B" --max-delete 20 2>&1 | grep -E "Bisync successful|ERROR" | head -3; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== mismos 4 borrados con el límite por defecto (sin --max-delete)"
rclone bisync "$A" "$B" -v 2>&1 | grep -E "Deleted|Bisync successful|ERROR" | head -6; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== borrar 5 más de 8 con el límite por defecto"
ls "$A" | head -5 | while read f; do rm "$A/$f"; done
rclone bisync "$A" "$B" 2>&1 | grep -E "Bisync successful|ERROR" | head -3; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== lo mismo con --force"
rclone bisync "$A" "$B" --force -v 2>&1 | grep -E "Deleted|Bisync successful|ERROR" | head -7; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"
echo "== --force con a completamente vacío"
rm -f "$A"/*
rclone bisync "$A" "$B" --force 2>&1 | grep -E "Bisync successful|ERROR" | head -2; echo "exit=${PIPESTATUS[0]}"
echo "ficheros en a: $(ls $A | wc -l), en b: $(ls $B | wc -l)"