Créer, vérifier et restaurer une sauvegarde de projet avec zip
Pour qu’une sauvegarde ZIP reflète l’état actuel de votre projet, créez une nouvelle archive à
chaque exécution. Exécuter zip -r sur une archive existante ajoute ou met à jour des entrées, mais
peut conserver des fichiers que vous avez supprimés de la source. Le
manuel d’Info-ZIP documente ce comportement. Ci-dessous, un script
Bash construit une nouvelle archive, la vérifie, et ne remplace qu’ensuite la sauvegarde précédente.
Ce guide cible Linux avec Bash, Info-ZIP Zip 3.0, UnZip 6.0, GNU coreutils, findutils et diffutils.
Vérifiez les versions d’Info-ZIP avec zip -v et unzip -v. Le script utilise mv -T de GNU, il
doit donc être adapté pour macOS. Make est facultatif.
Arrêtez les builds et les autres processus qui écrivent dans le projet avant l’archivage. Le
résultat représente un répertoire qui reste inchangé pendant l’exécution ; zip ne prend pas
d’instantané du système de fichiers. Utilisez des fichiers ordinaires, des répertoires et des liens
symboliques dont les noms ne contiennent aucun caractère de contrôle.
Préparer un petit projet
Exécutez ce bloc dans Bash. Il crée un projet jetable et refuse de réutiliser un répertoire
zip-demo existant. Les parenthèses maintiennent votre shell dans son répertoire d’origine.
(
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
)
Créer une nouvelle archive à chaque exécution
Enregistrez ce script sous zip-demo/backup.sh. Il archive project_directory par rapport à son répertoire de
travail. Placez le script, l’archive temporaire et l’archive finale à côté du projet, en dehors de
l’arborescence source.
#!/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"
Exécutez-le depuis le répertoire où vous avez créé zip-demo :
(cd zip-demo && bash backup.sh)
En cas de succès, la dernière ligne indique project_backup_YYYY-MM-DD.zip, avec la date locale de la machine.
Une exécution réussie le même jour remplace ce fichier sans confirmation. Les fichiers ajoutés
apparaissent, les fichiers modifiés sont relus et les fichiers supprimés disparaissent, même
lorsqu’une modification conserve la taille et la date de modification d’un fichier. Avec des entrées
inchangées, le contenu restauré reste le même ; des octets d’archive identiques ne sont pas garantis.
La destination n’est remplacée qu’après la réussite de la compression et de la vérification. Une source manquante, un fichier illisible ou une vérification échouée renvoie un statut non nul sans le message de succès et laisse la sauvegarde précédente en place. Un répertoire ou un lien symbolique en destination est rejeté. Exécutez une seule sauvegarde à la fois, dans un répertoire que vous contrôlez.
La commande find initiale vérifie que toute l’arborescence source peut être parcourue, y compris
les répertoires exclus. C’est important, car zip peut ignorer silencieusement un répertoire
illisible, même avec -MM.
Chaque option a un rôle précis : -r parcourt le répertoire, -MM rend fatals les fichiers
d’entrée manquants ou illisibles, et -y stocke les liens symboliques plutôt que leurs cibles.
Mettre les motifs d’exclusion entre guillemets laisse la correspondance à zip. Ce sont des
options d’Info-ZIP.
Le script efface les options ZIP/UnZip héritées afin que les valeurs par défaut du shell ne puissent
pas modifier silencieusement son comportement.
mv -fT de GNU remplace le fichier de destination en le traitant comme un
chemin de fichier plutôt que comme un répertoire dans lequel déplacer l’archive.
Choisir ce qu’il faut exclure
L’exemple omet .git, node_modules et dist à n’importe quelle profondeur, les noms se
terminant par .tmp ou .temp, ainsi que .DS_Store. La correspondance couvre aussi les
répertoires portant ces noms et leur contenu.
Par exemple, */node_modules/* correspond à la fois à project_directory/node_modules/omit.txt et aux dépendances d’un paquet
imbriqué.
Ces exclusions conviennent à la transmission d’un code source dont les dépendances et la sortie de build peuvent être recréées. Supprimez des exclusions si ces fichiers font partie de ce que vous devez restaurer. Les fichiers cachés (dotfiles) sont par ailleurs inclus : inspectez donc l’archive avant de la partager, car cette liste n’est pas un détecteur de secrets. Si vous la modifiez, modifiez aussi les exclusions de comparaison ci-dessous.
Exécuter le même script depuis Make
Si vous le souhaitez, enregistrez ceci sous zip-demo/Makefile, avec une tabulation avant bash.
Exécutez make -C zip-demo archive depuis le répertoire parent. La cible factice (phony) s’exécute à chaque fois, et
Make signale l’échec du script comme l’échec de la cible.
.PHONY: archive
archive:
bash backup.sh
Restaurer et comparer les fichiers
Exécutez ce bloc depuis le répertoire parent de zip-demo, le même jour que la sauvegarde. Pour une
sauvegarde plus ancienne, remplacez l’expression de date par son nom de fichier réel. Ne restaurez
qu’une archive en laquelle vous avez confiance.
(
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 liste des entrées doit contenir src/app.txt et read me.txt sous project_directory/, sans les
dépendances ni les fichiers générés. L’arborescence restaurée conserve ce répertoire de premier
niveau.
diff ne produit aucune sortie et se termine avec le code zéro lorsque le contenu des fichiers
inclus, la structure des répertoires et les cibles des liens symboliques correspondent. Cette
comparaison ne tient compte ni des propriétaires, ni des permissions, ni des horodatages.
mkdir restored échoue délibérément si cette destination existe déjà, avant que l’extraction n’y touche.
Pour répéter une restauration, choisissez un nouveau nom de répertoire dans la commande d’extraction
et dans celle de comparaison.
Si l’extraction échoue en cours de route, gardez ce répertoire partiel à part et réessayez dans un
nouveau répertoire.
Le manuel d’UnZip explique que -t décompresse les entrées et vérifie leurs CRC
stockés. Cela détecte les données endommagées, mais ne prouve pas que vous avez inclus les bons
fichiers. La restauration et la comparaison distinctes vérifient ce point, à condition que la source
soit restée inchangée depuis la sauvegarde.
Choisir un niveau de compression
Modifiez -6 dans le script lorsque vous souhaitez comparer les niveaux :
| Option | Objectif |
|---|---|
-0 | Stocker les fichiers sans compression |
-1 | Privilégier la vitesse de compression |
-6 | Utiliser le niveau de compression par défaut d’Info-ZIP |
-9 | Consacrer plus de temps à obtenir une archive plus petite |
Les options de niveau de compression contrôlent l’effort de compression. Il n’existe pas de pourcentage de gain fixe, et un niveau plus élevé ne produit pas nécessairement une archive sensiblement plus petite. Comparez le temps écoulé et la taille de l’archive sur votre projet réel, puis restaurez chaque archive candidate. N’oubliez pas que ce script remplace la sortie du même jour : enregistrez donc chaque candidate séparément si vous voulez la conserver.
Chiffrer séparément si nécessaire
Cette archive n’est pas chiffrée. Avec Info-ZIP, zip -e utilise un chiffrement ZIP traditionnel
faible ; la FAQ d’Info-ZIP met en garde contre ses limites.
Pour les données sensibles, choisissez un processus de chiffrement distinct, comme
age, qui prend en charge les clés de destinataire et les phrases secrètes. Vérifiez
que le destinataire peut déchiffrer et restaurer l’archive avant de vous fier à ce processus. Le
chiffrement laisse aussi le ZIP d’origine non chiffré sur le disque, sauf si vous gérez cette copie
séparément.
Gérer les échecs et les limites
Si une exécution échoue, examinez son message de diagnostic et son code de sortie ; un ancien ZIP à la destination ne prouve pas que la tentative du jour a réussi. Prévoyez assez d’espace libre pour contenir simultanément l’archive précédente et la nouvelle. Lors d’un échec ordinaire, le trap de sortie supprime le répertoire temporaire ; un arrêt forcé ou une coupure de courant peut le laisser en place.
Cet exemple vérifie le contenu et les chemins des fichiers d’un projet sous Linux. Il ne constitue pas un format complet de sauvegarde système pour les ACL, les attributs étendus, les fichiers spéciaux ou les relations de liens physiques. Les archives volumineuses peuvent nécessiter la prise en charge de Zip64 dans l’outil d’extraction du destinataire. Conservez les anciennes sauvegardes réussies selon vos propres besoins de rétention, et testez la restauration avec les outils qui les utiliseront réellement.
