Introduction

On présente ici un script bash qui permet d'effectuer une copie d'un fichier ou d'un dossier par commande “rsync”.

Ce script peut être utilisé de manière autonome, mais on va de plus présenter un second script, cette fois en langage Python, pour encapsuler un ou plusieurs appels du script précédent dans une véritable application avec fenêtre graphique dans laquelle on pourra :

  • appeler le script bash en mode “sauvegarde”,
  • appeler le script bash en mode “restauration” (copies inverses des précédentes),
  • lister et vérifier le déroulement des opérations dans une zone déroulante.

Avec de tels scripts on pourra facilement se construire des outils permettant en quelques clics de dupliquer autant de fichiers et dossiers que souhaités, d'un disque à un autre, d'un disque vers une clé USB, etc…

Le principe d'encapsulation de scripts dans une fenêtre Python est relativement simple, et permet de créer toutes sortes d'outils sans forcément maîtriser toutes les subtilités de Python. Ici on pourra par exemple personnaliser l'outil présenté sans rien connaître du tout de Python, il suffira d'adapter quelques lignes à ses besoins.

Exemple d'application créée par les scripts de cette page :

Exemple d'outil

Pour ceux qui voudront en savoir plus, on notera que la fenêtre créée dans cet exemple s'appuie sur Pmw, une boîte-à-outils de “widgets TkInter” eux-mêmes fondés sur “Tcl/Tk”. Le langage Python est probablement l'un des plus géniaux qui aie jamais existé, totalement orienté objet, doté d'innombrables bibliothèques dans tous les domaines; on trouvera facilement des infos à ce sujet sur le net.

A noter : un autre mode de sauvegarde, dit “incrémental”, est présenté par Jean-Luc ici :
http://root66.freecontrib.org/wiki/doku.php?id=la_sauvegarde_incrementale ;
on pourra choisir l'un ou l'autre selon ses besoins.

Article rédigé par Jeanmm 8-)
version initiale : 21 mai 2006.

Le script bash

Voici un script qui permet principalement d'effectuer des copies optimisées par commande “rsync”; il permet accessoirement de lister la place occupée par un dossier.

Lire les explications incluses dans le script (lire aussi les explications complémentaires en fin de ce document) :

 
#!/bin/sh
#
# Archivage sécurisé et optimisé de fichiers
# ------------------------------------------
#
# Copie réentrante d'un dossier source ou d'un fichier vers un dossier
# cible (typiquement une clé USB, mais pas forcément),
# par commande rsync et avec "checksum".
#
# Auteur  : jeanmm
# Date    : juillet 2005
# Licence : GPL
# Lien    : http://www.root66.net/accueil/
 
# 1er argument = dossier contenant le dossier ou fichier à copier
# ---------------------------------------------------------------
 
# On peut y mettre "du" pour simplement effectuer un "du" du dossier cible
# (indication de l'espace occupé); dans ce cas mettre 3 arguments :
# "du" + arg-source-quelconque + dossier-cible
 
if [ "0$1" == "0" ] ; then
  echo "Indiquer en argument 1 le dossier contenant le dossier ou fichier à copier"
  exit
else
  dep=$1
fi
 
# 2ème argument = dossier source (ou fichier) à copier
# ----------------------------------------------------
 
if [ "0$2" == "0" ] ; then
  echo "Indiquer en argument 2 le dossier ou fichier à copier"
  exit
else
  source=$2
fi
 
# 3ème argument = dossier cible
# -----------------------------
 
# On peut terminer par "/" selon les règles rsync
# par exemple pour une clé USB : /mnt/removable/
 
if [ "0$3" == "0" ] ; then
  echo "Indiquer en argument 3 le dossier cible"
  exit
else
  dest=$3
fi
 
# Cas de l'option "du" : Info sur le contenu
# ------------------------------------------
 
if [ "0$1" == "0du" ] ; then
  echo "Contenu du dossier cible (en Ko) :"
  echo "----------------------------------"
  du -h -k --max-depth=1 $dest | sort -n
  exit 0
fi
 
# Cas de Copie effective
# ----------------------
 
# rsync suivi de sync pour forcer l'écriture immédiate
# options de rsync :
# - v : mode bavard (liste les noms)
# - u : update (n'écrase pas les fichiers plus récents)
# - r : récursif
# - t : conserve les dates
# --modify-window 1 : tolérance d'une seconde pour l'égalité des dates
#                     utile pour les fichiers FAT
 
echo "Copie de $dep$source vers $dest"
echo "-----------------------------------------------------------------------"
rsync -vurt --modify-window 1 $dep$source $dest
sync

Dans la suite de cet article le script Bash est supposé enregistré dans /usr/local/OutilArchivage/OutilArchivage.sh. On pourra bien entendu l'enregistrer à tout endroit de sa convenance, et avec le nom souhaité. L'essentiel est qu'il soit exécutable par les utilisateurs autorisés.

Installation de Python, TkInter et Pmw

Python

On suppose que l'installation de Python ne pose aucun problème. Normalement les distributions Linux fournissent Python par défaut, c'est en effet une brique de base pour de nombreux autres logiciels.

TkInter

C'est une bibliothèque d'objets graphiques de base servant à programmer des applications en Python; elle s'appuie sur Tcl/Tk. Elle doit être installée si ce n'est déjà fait. Pour une Mandriva il suffit de taper “urpmi tkinter” dans une console root. Pour plus d'infos : http://wiki.python.org/moin/TkInter.

Pmw

C'est une bibliothèque d'objets graphiques plus élaborés, par association d'objets TkInter. Pour l'installer il suffit de se procurer l'archive “Pmw.1.2.tar.gz” sur http://pmw.sourceforge.net/ et de mettre son contenu dans /usr/lib/pythonXXX/site-packages/ (avec XXX = la version de Python installée, par exemple python2.4).

Le script Python

Voici un exemple de script qui produit la fenêtre présentée dans l'introduction (voir les explications après le script) :

 
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
 
# Script Python de création de fenetre basique pour
# lancer un script bash et lister ses sorties
# -------------------------------------------------
 
# Cet outil affiche une fenetre de paramétrage et de
# lancement d'un script bash qui permet plusieurs
# manips pour archiver des fichiers par exemple sur clé USB.
#
# Le script est supposé terminé quand il n'y a plus
# de fichier stdout/stderr, et les sorties éventuelles
# dans ces 2 fichiers sont captées et affichées dans
# une zone de texte déroulante.
 
# Auteur  : jeanmm
# Date    : juillet 2005
# Licence : GPL
# Lien    : http://www.root66.net/accueil/
 
import sys, os, string
from   Tkinter import *
import Pmw
 
#===========================================
# Début de la partie à adapter à ses besoins
#===========================================
 
# Titre de la fenetre
 
MonTitre1  = "Archivages sur Cle USB"
 
# Commentaire facultatif en début de la zone déroulante
 
commentaire = """
Cet outil sert à archiver des fichiers sur clé USB
--------------------------------------------------
 
Connectez une clé USB, attendez quelques secondes, puis
appuyez sur le bouton de votre choix :
- Copier les fichiers,
- Restaurer les fichiers archivés
  (inverse de la copie précédente).
 
Seuls les fichiers nouveaux ou plus récents seront copiés.
Pour encore plus de sécurité on pourra alterner les
archivages sur 2 clés distinctes.
"""
 
# Dossiers
 
dossierSource = '/home/jean/'
dossierCible  = '/mnt/removable/perso/'
dossierOutil  = '/usr/local/OutilArchivage/'
 
# Indiquer ici la liste des fichiers et/ou dossiers à copier
 
sources = 'GnuCash Musique Documents'
 
# Le script bash à exécuter
 
MonScript1 = dossierOutil + 'OutilArchivage.sh'
 
# Logo pour agrémenter la fenêtre
 
MonLogo = dossierOutil + 'Logo.gif'
 
#===========================================
# Fin de la partie à adapter à ses besoins
#===========================================
 
 
# Sous-programmes généraux
# ========================
 
# Rafraichissement de fenêtre en cas de déplacement
# ------------------------------------------------
 
def rafraichissement(ev):
    fen1.update_idletasks()
 
# quitter le programme
# --------------------
 
# on simule le clic sur le bouton quand il est sélectionné + touche entrée
def quitter(ev=None):
    bouton2.invoke()
 
 
# Archivage des fichiers
# ----------------------
 
def Archiver_mes_fichiers(ev=None):
 
    fen1.update_idletasks()
    zoneListage.insert(END, '\n')
 
# Test de l'existence des dossiers
 
    if not os.path.isdir(dossierSource):
        ligne="Attention : le dossier " + dossierSource + " n'existe pas ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return
    if not os.path.isdir(dossierCible):
        ligne="Attention : le dossier " + dossierCible + " n'existe pas (clé non insérée) ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return
 
# Lancement du script avec args dossier-source + source + dossier-destination
 
    liste = string.split(sources,' ')
 
    for source in liste:
        fin,fout=os.popen4(MonScript1 + ' ' + dossierSource + ' ' + source + ' ' + dossierCible)
 
# Boucle de récupération des résultats (txt='' => terminé)
 
        txt='x'   # Contenu de chaque ligne produite (stdout + stderr)
 
        while txt:
            txt=fout.readline()
            if txt:
                ligne='%s' %(txt)
            else:
                ligne='\n'
            if ligne[0:9]=='Copie de ':
                zoneListage.insert(END, ligne, 'rouge')
            elif string.find(ligne,'building file list')>=0:
                pass
            elif string.find(ligne,'bytes/sec')>0:
                pass
            elif string.find(ligne,'speedup is')>0:
                pass
            else:
                zoneListage.insert(END, ligne)
            zoneListage.see(END)
            fen1.update_idletasks()
        fin.close()
        fout.close()
 
# Lancement du script avec args "du" + (dummy-source) + dossier-destination
# pour info sur occupation dossier cible
 
    fin,fout=os.popen4(MonScript1 + ' du x ' + dossierCible)
 
# Boucle de récupération des résultats (txt='' => terminé)
 
    txt='x'   # Contenu de chaque ligne produite (stdout + stderr)
 
    while txt:
        txt=fout.readline()
        if txt:
            ligne='%s' %(txt)
        else:
            ligne='\n'
        if ligne[0:18]=='Contenu du dossier':
            zoneListage.insert(END, ligne, 'rouge')
        elif string.find(ligne,'building file list') >=0:
            pass
        else:
            zoneListage.insert(END, ligne)
        zoneListage.see(END)
        fen1.update_idletasks()
    fin.close()
    fout.close()
 
    ligne='Copies terminées\n'
    zoneListage.insert(END, ligne, 'rouge')
 
 
# Restauration des fichiers
# -------------------------
 
def Restaurer_mes_fichiers(ev=None):
 
    fen1.update_idletasks()
    zoneListage.insert(END, '\n')
 
# Test de l'existence des dossiers
 
    if not os.path.isdir(dossierCible):
        ligne="Attention : le dossier " + dossierCible + " n'existe pas (clé non insérée) ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return
    if not os.path.isdir(dossierSource):
        ligne="Attention : le dossier " + dossierSource + " n'existe pas ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return
 
# Lancement du script avec args dossier-source + source + dossier-destination
 
    liste = string.split(sources,' ')
 
    for source in liste:
        fin,fout=os.popen4(MonScript1 + ' ' + dossierCible + ' ' + source + ' ' + dossierSource)
 
# Boucle de récupération des résultats (txt='' => terminé)
 
        txt='x'   # Contenu de chaque ligne produite (stdout + stderr)
 
        while txt:
            txt=fout.readline()
            if txt:
                ligne='%s' %(txt)
            else:
                ligne='\n'
            if ligne[0:9]=='Copie de ':
                zoneListage.insert(END, ligne, 'rouge')
            elif string.find(ligne,'building file list')>=0:
                pass
            elif string.find(ligne,'bytes/sec')>0:
                pass
            elif string.find(ligne,'speedup is')>0:
                pass
            else:
                zoneListage.insert(END, ligne)
            zoneListage.see(END)
            fen1.update_idletasks()
        fin.close()
        fout.close()
 
# Lancement du script avec args "du" + (dummy-source) + dossier-destination
# pour info sur place occupée par le dossier cible
 
    fin,fout=os.popen4(MonScript1 + ' du x ' + dossierSource)
 
# Boucle de récupération des résultats (txt='' => terminé)
 
    txt='x'   # Contenu de chaque ligne produite (stdout + stderr)
 
    while txt:
        txt=fout.readline()
        if txt:
            ligne='%s' %(txt)
        else:
            ligne='\n'
        if ligne[0:18]=='Contenu du dossier':
            zoneListage.insert(END, ligne, 'rouge')
        else:
            zoneListage.insert(END, ligne)
        zoneListage.see(END)
        fen1.update_idletasks()
    fin.close()
    fout.close()
 
    ligne='Restaurations terminées\n'
    zoneListage.insert(END, ligne, 'rouge')
 
 
# ----------- Programme principal -----------
 
# Instanciation d'une fenêtre Pmw (Python méga-widgets)
# -----------------------------------------------------
 
fen1 = Pmw.initialise()
fen1.title(MonTitre1)
fen1.monOption=IntVar()
 
fen1.bind("<Alt-F4>", quitter)
fen1.bind("<Configure>", rafraichissement)
 
 
# creation des widgets de configuration/lancement du script
# =========================================================
 
# bouton pour archiver
bouton1a = Button(fen1, bg='DeepSkyBlue4', activebackground='DeepSkyBlue2', fg='white', width=20, text="Copier les Fichiers", command=Archiver_mes_fichiers)
bouton1a.grid(row=1, column=1, padx=2, pady=4)
 
# bouton pour restaurer
bouton1b = Button(fen1, bg='DeepSkyBlue4', activebackground='DeepSkyBlue2', fg='white', width=20, text="Restaurer les Fichiers", command=Restaurer_mes_fichiers)
bouton1b.grid(row=2, column=1, padx=2, pady=4)
 
# Bouton pour quitter
bouton2 = Button(fen1, bg='DeepSkyBlue4', activebackground='DeepSkyBlue2', fg='white', width=20, text='Quitter', command=fen1.quit)
bouton2.grid(row=3, column=1, padx=2, pady=4)
bouton2.bind("<Return>",quitter)
 
texte1 = Label(fen1, text='Dossier source : ' + dossierSource)
texte1.grid(row=1, column=2, sticky=W, padx=2, pady=4)
 
texte2 = Label(fen1, text='Dossier cible : ' + dossierCible)
texte2.grid(row=2, column=2, sticky=W, padx=2, pady=4)
 
texte3 = Label(fen1, text='Copie de : ' + sources)
texte3.grid(row=3, column=2, sticky=W, padx=2, pady=4)
 
 
# Logo en bout de lignes 1 à 4
# ----------------------------
 
can1 = Canvas(fen1, width=90, height=106, bg='AntiqueWhite3')
photo = PhotoImage(file=MonLogo)
item = can1.create_image(45, 53, image=photo)
can1.grid(row=1, column=3, rowspan=4, padx=4, pady=6)
 
 
# Zone de listage des résultats du script
# ---------------------------------------
 
# C'est un rectangle de la largeur totale de la fenêtre,
# avec ascenseurs automatiques si nécessaire.
 
zoneListage = Pmw.ScrolledText(fen1,
    text_font   = 'Courier 11 normal', text_bg = 'AliceBlue',
    text_padx   = 10, text_pady = 10, text_wrap = 'none',
    borderframe = 1,
    borderframe_borderwidth = 3,
    borderframe_relief = RIDGE,
    usehullsize = 1,
    hull_width = 730, hull_height = 350)
 
zoneListage.grid(row=6, column=1, columnspan=3, padx=5, pady=5)
zoneListage._textbox['takefocus'] = 0
 
# balise pour lignes rouges créées ici (en plus
# des lignes noires générées par le script)
 
zoneListage.tag_configure('rouge', foreground='red')
zoneListage.insert(END, commentaire)
 
 
# Lancement de l'application
# --------------------------
 
fen1.mainloop()

Explications

Les seules lignes à modifier (surtout si on n'est pas à l'aise avec Python et Pmw) se situent entre :

#===========================================
# Début de la partie à adapter à ses besoins
#===========================================

et

#===========================================
# Fin de la partie à adapter à ses besoins
#===========================================

dossierSource

C'est à partir de ce dossier qu'on lira les dossiers et fichiers à copier. Par exemple ici :

dossierSource = '/home/jean/'

dossierCible

C'est l'endroit où on va stocker la copie des fichiers et dossiers trouvés dans le dossier source. Respecter la syntaxe pour une cible par commande rsync (faire “man rsync” pour les détails), par exemple le résultat est différent selon qu'on met ou pas un ”/” en fin. Je conseille de mettre ”/” à la fin des 2 dossiers dossierSource et dossierCible, ces 2 dossiers devant déjà exister.

Pour une clé USB le nom de dossierCible typique est '/mnt/removable/', mais on pourra choisir toute cible de son choix, sur disque, disquette, un sous-dossier de clé, etc… Exemple :

dossierCible  = '/mnt/removable/perso/'

dossierOutil

On met ici l'endroit où sont les différents fichiers nécessaires à l'outil lui-même :

  • OutilArchivage.py : ce script Python contenant l'outil (nom au choix, suffixe .py recommandé)
  • OutilArchivage.sh : le script bash contenant la commande rsync (nom de fichier exactement égal au nom déclaré dans le script Python)
  • Logo.gif : image affichée pour agrémenter l'outil (nom de fichier exactement égal au nom déclaré dans le script Python)

Exemple :

dossierOutil = '/usr/local/OutilArchivage/'
MonScript1   = dossierOutil + 'OutilArchivage.sh'
MonLogo      = dossierOutil + 'Logo.gif'

Remarques :

  • un logo typique “pèse” de l'ordre de 100×100 pixels. On peut aussi ne pas en mettre en effaçant les lignes correspondantes notamment en fin de script.
  • il est nécessaire que les fichiers existent, sinon le script Python “plantera”; de toute façon il est recommandé de lancer le premier script dans une console pour vérifier son comportement (voir le paragraphe “Recommandations” plus bas pour la façon de faire).

sources

Indiquer ici la liste des dossiers et fichiers à copier, séparés par un blanc. Il est donc conseillé de ne pas indiquer de noms contenant eux-mêmes des blancs (d'ailleurs sur Linux il vaut mieux ne jamais utiliser de tels noms). La seule limitation vient de la place disponible pour tout copier; en particulier, tous les dossiers sont copiés intégralement, y compris les sous-dossiers. L'outil indique en fin de chaque utilisation la taille totale occupée par le dossier cible et chacun de ses sous-dossiers. Exemple :

sources = 'GnuCash Musique Documents'

Commentaires

On peut modifier le titre de la fenêtre par les lignes :

# Titre de la fenetre
   MonTitre1  = "Archivages sur Cle USB"

On peut aussi spécifier le texte listé en début de zone déroulante : c'est le texte entre les triples quotes dans les lignes :

# Commentaire facultatif dans la zone déroulante
 
   commentaire = """
   Cet outil sert à archiver des fichiers sur clé USB
   ... etc ...
   """

Création d'outils spécialisés

On pourra se créer plusieurs copies spécialisées de l'outil selon ses besoins particuliers, par exemple une pour archiver ses comptes, une autre pour ses images, une pour tout son dossier home, etc…

Normalement le script Bash est unique, seul le script Python est à dupliquer/modifier pour chaque outil.

L'utilisation de rsync garantit une copie optimisée, seuls les fichiers nouveaux et modifiés étant traités, et même les portions identiques de gros fichiers ne seront pas recopiées à chaque fois.

Pour plus de sécurité, on pourra alterner des copies sur des supports différents, par exemple 2 clés USB distinctes. Par ailleurs rien n'interdit de vérifier de temps en temps le résultat !

Variantes dans les scripts

Par rapport aux options prises en fin de script bash, on pourra par exemple ajoûter ”–delete” dans la commande rsync :

rsync -vurt --delete --modify-window 1 $dep$source $dest

La différence peut être fondamentale, surtout en cas de restauration, cela peut mener à des pertes de données :

  • si on a effacé par erreur un fichier depuis un certain temps et qu'on a lancé rsync entretemps, le fichier sera aussi absent du dossier de sauvegarde;
  • si on restaure une ancienne sauvegarde, les nouveaux fichiers qu'on a pu créer entretemps et qui ne figurent pas dans la sauvegarde seront automatiquement effacés du disque.

Inversement, si on ne met pas “delete”, les anciens fichiers resteront en permanence dans le dossier de sauvegarde, ce qui peut d'une part le faire “grossir” progressivement, et en cas de restauration regénérer des vieux fichiers dont on n'a pas forcément besoin. Dans le doute il vaut mieux éviter ”–delete” et avoir plutôt trop de fichiers que de perdre des données importantes.

Il existe une autre option assez commune, ”-a” ou ”–archive”, qui est équivalente à ”-rlptgoD” (voir la doc de rsync pour les détails). C'est un moyen rapide de dire qu'on veut la récursion et tout préserver; le script de l'exemple ci-dessus n'en reprend que les options “r” et “t”. A noter que préserver le propriétaire n'est pas possible pour une partition cible FAT (en particulier les clés USB), car Linux leur attribue un propriétaire unique fictif au moment du montage, ce qui peut provoquer des messages d'erreurs de type “chown” si les fichiers sources appartiennent à un autre utilisateur.

Avant la commande “rsync” on pourra par ailleurs mettre une commande de création du dossier cible, par exemple :

mkdir -p $dest

Si on choisit cette option, il faut aussi effacer du script Python les lignes qui testent l'existence du dossier cible :

    if not os.path.isdir(dossierCible):
        ligne="Attention : le dossier " + dossierCible + " n'existe pas (clé non insérée) ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return

On pourra cependant laisser un test minimal dans le script Python, par exemple pour vérifier le montage d'une clé USB :

    if not os.path.isdir('/mnt/removable'):
        ligne="Attention : le dossier '/mnt/removable' n'existe pas (clé non insérée) ?\n"
        zoneListage.insert(END, ligne, 'rouge')
        return

Un autre aspect simple à modifier : la taille de la zone déroulante qui liste les messages du script. Il suffit de modifier la ligne suivante en fin de script Python, “hull_width” étant la largeur et “hull_height” la hauteur en “pixels” :

hull_width = 730, hull_height = 350)

De toute façon, quelle que soit la taille du texte à afficher, Pmw fait en sorte de doter la zone déroulante d'ascenseurs vertical et/ou horizontal si nécessaire, c'est un exemple de création d'objets TkInter plus simples de manière automatique en cas de besoin.

En fait tout est modifiable, il suffit d'éditer le script Python ou le script Bash et les nouveautés sont appliquées immédiatement au prochain lancement de l'outil, sans nécessiter aucune procédure de recompilation ou quoi que ce soit : c'est l'une des grandes forces de ces langages de scripts.

Recommandations

Premiers essais

Lors des premiers essais d'un de ces outils, il est fortement recommandé de le lancer en ligne de commande dans une console, ça permet de voir d'éventuels messages d'erreurs et de les corriger. Le plus simple est de se mettre dans le dossier qui contient les scripts, et de taper par exemple :

chmod +x OutilArchivage.py     (commandes à effectuer une seule fois pour rendre les scripts exécutables)
chmod +x OutilArchivage.sh

puis une des commandes :

./OutilArchivage.py

ou bien

python OutilArchivage.py

Lorsque l'outil est au point on pourra alors le lancer par d'autres moyens, par exemple par une simple icône sur le bureau.

Important : en effaçant ou modifiant des lignes dans le script Python, faire très attention de bien respecter l'indentation en début de chaque ligne, qui est ici un multiple de 4 caractères blancs. Changer l'indentation peut en effet provoquer des erreurs de compilation ou changer la logique du programme.

Validation

Un bon logiciel ne sert à rien tant qu'il n'a pas été testé réellement. Ce n'est qu'après quelques essais valables qu'on sera sûr que ça répond au besoin. Voici donc des manips conseillées, à faire dès que tout est installé proprement.

Restauration de fichiers effacés :

  • mettre par exemple quelques fichiers provisoires dans un dossier source,
  • effectuer une sauvegarde (bouton “Copier les Fichiers”),
  • effacer les fichiers provisoires du disque,
  • effectuer une restauration (bouton “Restaurer les fichiers”),
  • vérifier que les fichiers sont bien restaurés sur le disque et utilisables.

Non Restauration de fichiers récents :

  • mettre par exemple un fichier texte provisoire dans un dossier source,
  • effectuer une sauvegarde (bouton “Copier les Fichiers”),
  • modifier le fichier provisoire du disque par éditeur,
  • effectuer une restauration (bouton “Restaurer les fichiers”),
  • vérifier que le fichier modifié n'aie pas été écrasé par le fichier ancien de la sauvegarde,

et pour compléter ce test :

  • effectuer une sauvegarde (bouton “Copier les Fichiers”),
  • vérifier que le fichier plus récent aie bien été copié à la place de l'ancien.
 
sauvegarder_ses_donnees.txt · Dernière modification: 2017/02/21 15:10 (édition externe)
 
Sauf mention contraire, le contenu de ce wiki est placé sous la licence suivante:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki