Crea, verifica y restaura un respaldo de proyecto con zip
Para que un respaldo ZIP refleje el estado actual de tu proyecto, crea un archivo ZIP nuevo en cada
ejecución. Ejecutar zip -r sobre un archivo ZIP existente añade o actualiza
entradas, pero puede conservar archivos que eliminaste del origen. El
manual de Info-ZIP documenta este comportamiento. A continuación,
un script de Bash crea un archivo ZIP nuevo, lo verifica y solo entonces reemplaza el respaldo
anterior.
Esta guía está dirigida a Linux con Bash, Info-ZIP Zip 3.0, UnZip 6.0, GNU coreutils, findutils y
diffutils. Consulta zip -v y unzip -v para conocer las
versiones de Info-ZIP. El script usa mv -T de GNU, por lo que necesita
adaptaciones para macOS. Make es opcional.
Detén las compilaciones y otros procesos que escriban en el proyecto antes de crear el archivo ZIP.
El resultado representa un directorio que permanece sin cambios durante la ejecución;
zip no toma una instantánea del sistema de archivos. Usa archivos comunes,
directorios y enlaces simbólicos, sin caracteres de control en sus nombres.
Prepara un proyecto pequeño
Ejecuta este bloque en Bash. Crea un proyecto desechable y se niega a reutilizar un directorio
zip-demo existente. Los paréntesis mantienen tu shell en su directorio original.
(
set -euo pipefail
mkdir zip-demo
cd zip-demo
mkdir -p project_directory/src project_directory/node_modules project_directory/dist
printf 'version one\n' > project_directory/src/app.txt
printf 'project notes\n' > 'project_directory/read me.txt'
printf 'dependency\n' > project_directory/node_modules/omit.txt
printf 'generated\n' > project_directory/dist/omit.txt
)
Crea un archivo ZIP nuevo en cada ejecución
Guarda este script como zip-demo/backup.sh. Archiva project_directory con
una ruta relativa a su directorio de trabajo. Mantén el script, el archivo ZIP temporal y el archivo
ZIP final junto al proyecto, fuera del árbol de origen.
#!/usr/bin/env bash
set -euo pipefail
umask 077
unset ZIPOPT UNZIP UNZIPOPT
source_dir=project_directory
backup_date=$(date +%Y-%m-%d)
archive_name="project_backup_$backup_date.zip"
if [[ ! -d "$source_dir" || -L "$source_dir" ]]; then
printf 'Source must be a real directory: %s\n' "$source_dir" >&2
exit 1
fi
if [[ -L "$archive_name" || ( -e "$archive_name" && ! -f "$archive_name" ) ]]; then
printf 'Destination must be a regular file or absent: %s\n' "$archive_name" >&2
exit 1
fi
find "$source_dir" -type d -print > /dev/null
work_dir=$(mktemp -d './.zip-backup.XXXXXXXX')
trap 'rm -rf -- "$work_dir"' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
zip -q -6 -r -MM -y "$work_dir/archive.zip" "$source_dir" \
-x '*/.git' '*/.git/*' \
'*/node_modules' '*/node_modules/*' \
'*/dist' '*/dist/*' \
'*.tmp' '*.tmp/*' '*.temp' '*.temp/*' \
'*/.DS_Store' '*/.DS_Store/*'
unzip -tq "$work_dir/archive.zip"
mv -fT -- "$work_dir/archive.zip" "$archive_name"
printf 'Backup created: %s\n' "$archive_name"
Ejecútalo desde el directorio donde creaste zip-demo:
(cd zip-demo && bash backup.sh)
Si se completa correctamente, la última línea muestra el nombre project_backup_YYYY-MM-DD.zip
usando la fecha local del equipo. Una ejecución correcta el mismo día reemplaza ese archivo sin
pedir confirmación. Los archivos añadidos aparecen, los modificados se vuelven a leer y los
eliminados desaparecen, incluso cuando un cambio conserva el tamaño y la hora de modificación de
un archivo. Si las entradas no cambian, el contenido restaurado sigue siendo el mismo; no se
promete que los bytes del archivo ZIP sean idénticos.
El destino se reemplaza solo después de que la compresión y la verificación se completen correctamente. Si falta el origen, un archivo no se puede leer o la verificación falla, se devuelve un estado distinto de cero sin el mensaje de éxito y se conserva el respaldo anterior. Se rechaza un destino que sea un directorio o un enlace simbólico. Ejecuta un solo respaldo a la vez en un directorio que controles.
La comprobación inicial con find verifica que se pueda recorrer todo el
árbol de origen, incluidos los directorios excluidos. Esto importa porque
zip puede omitir silenciosamente un directorio que no se puede leer,
incluso con -MM. Las opciones cumplen funciones específicas:
-r recorre el directorio, -MM hace que los
archivos de entrada ausentes o ilegibles provoquen un error fatal y -y
almacena los enlaces simbólicos en lugar de sus destinos. Poner entre comillas los patrones de
exclusión deja la búsqueda de coincidencias a zip. Estas son
opciones de Info-ZIP.
El script elimina las opciones heredadas de ZIP/UnZip para que los valores predeterminados del
shell no puedan cambiar su comportamiento silenciosamente.
mv -fT de GNU reemplaza el archivo de destino,
tratándolo como una ruta de archivo y no como un directorio al que moverlo.
Elige qué excluir
El ejemplo omite .git, node_modules y
dist a cualquier profundidad, los nombres que terminan en
.tmp o .temp, y .DS_Store.
Las coincidencias también abarcan los directorios con esos nombres y su contenido. Por ejemplo,
*/node_modules/* coincide tanto con project_directory/node_modules/omit.txt como con las
dependencias de un paquete anidado.
Estas exclusiones son apropiadas para entregar código fuente cuyas dependencias y resultados de compilación se pueden recrear. Elimina exclusiones si esos archivos forman parte de lo que necesitas restaurar. Por lo demás, se incluyen los archivos con nombres que comienzan con un punto, así que inspecciona el archivo ZIP antes de compartirlo: esta lista no es un detector de secretos. Si la cambias, cambia también las exclusiones de la comparación que aparece más adelante.
Ejecuta el mismo script desde Make
Opcionalmente, guarda esto como zip-demo/Makefile, con una tabulación antes de
bash. Ejecuta make -C zip-demo archive desde el directorio padre.
El objetivo ficticio se ejecuta siempre y Make informa que el objetivo falló si el script falla.
.PHONY: archive
archive:
bash backup.sh
Restaura y compara los archivos
Ejecuta este bloque desde el directorio padre de zip-demo el mismo día del
respaldo. Para un respaldo anterior, reemplaza la expresión de fecha por su nombre de archivo real.
Restaura únicamente un archivo ZIP en el que confíes.
(
set -euo pipefail
cd zip-demo
unset UNZIP UNZIPOPT ZIPINFO ZIPINFOOPT
archive_name="project_backup_$(date +%Y-%m-%d).zip"
unzip -tq "$archive_name"
unzip -Z1 "$archive_name"
mkdir restored
unzip -q "$archive_name" -d restored
diff -r --no-dereference \
-x .git -x node_modules -x dist -x '*.tmp' -x '*.temp' -x .DS_Store \
project_directory restored/project_directory
)
La lista de entradas debería contener src/app.txt y
read me.txt dentro de project_directory/, sin los archivos de
dependencias ni los generados. El árbol restaurado conserva ese directorio de nivel superior.
diff no produce ninguna salida y termina con un estado de cero cuando
coinciden el contenido de los archivos incluidos, la estructura de directorios y los destinos de
los enlaces simbólicos. No compara propietarios, permisos ni marcas de tiempo.
mkdir restored falla deliberadamente si ese destino ya existe, antes de que la
extracción lo modifique. Para repetir una restauración, elige un nombre de directorio nuevo tanto
en el comando de extracción como en el de comparación. Si la extracción falla a mitad del proceso,
mantén separado ese directorio parcial y vuelve a intentarlo en uno nuevo.
El manual de UnZip explica que -t
descomprime las entradas y comprueba sus CRC almacenados. Eso detecta datos dañados; no demuestra
que hayas incluido los archivos correctos. La restauración y la comparación por separado verifican
esa parte, siempre que el origen haya permanecido sin cambios desde el respaldo.
Elige un nivel de compresión
Cambia -6 en el script cuando quieras comparar niveles:
| Opción | Propósito |
|---|---|
-0 | Almacenar archivos sin compresión |
-1 | Priorizar la velocidad de compresión |
-6 | Usar el nivel de compresión predeterminado de Info-ZIP |
-9 | Dedicar más tiempo a intentar reducir el tamaño mediante compresión |
Las opciones de nivel de compresión controlan el esfuerzo de compresión. No hay un porcentaje de ahorro fijo y un nivel más alto no necesariamente produce un archivo ZIP significativamente más pequeño. Compara el tiempo transcurrido y el tamaño del archivo ZIP con tu proyecto real y luego restaura cada versión candidata. Recuerda que este script reemplaza el resultado del mismo día, así que guarda cada versión candidata por separado si quieres conservarla.
Cifra por separado cuando sea necesario
Este archivo ZIP no está cifrado. La opción zip -e de Info-ZIP usa el
cifrado ZIP tradicional, que es débil; las
preguntas frecuentes de Info-ZIP advierten sobre sus limitaciones.
Para material confidencial, elige un flujo de trabajo de cifrado independiente, como
age, que admite claves de destinatario y frases de contraseña.
Confirma que el destinatario pueda descifrarlo y restaurarlo antes de confiar en ese flujo de
trabajo. El cifrado también deja el ZIP original sin cifrar en el disco, a menos que gestiones esa
copia por separado.
Gestiona los fallos y los límites
Si una ejecución falla, inspecciona su diagnóstico y su estado de salida; un ZIP anterior en el destino no demuestra que el intento de hoy haya funcionado. Mantén suficiente espacio libre para el archivo ZIP anterior y el nuevo al mismo tiempo. Ante un fallo normal, el manejador de salida elimina el directorio temporal; una terminación forzada o un corte de energía pueden dejarlo allí.
Este ejemplo comprueba el contenido y las rutas de los archivos de un proyecto en Linux. No establece un formato completo de respaldo del sistema que abarque ACL, atributos extendidos, archivos especiales o relaciones entre enlaces duros. Los archivos ZIP grandes pueden requerir compatibilidad con Zip64 en la herramienta de extracción del destinatario. Conserva los respaldos anteriores que se hayan completado correctamente según tus propias necesidades de retención y prueba la restauración con las herramientas que realmente los vayan a utilizar.
