Ora che il progetto del programma è stabile, si può iniziare a scrivere il codice, che altro non è che una implementazione della soluzione cercata.
Esempio 10.1. Script di backup - Prima versione
#!/usr/bin/python # Filename: backup_ver1.py import os import time # 1. I file e le directory che sono oggetto del backup vengono specificate in una lista source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Se si sta usando Windows, usare source = [r'C:\Documents', r'D:\Work'] o qualcosa di simile # 2. Il backup deve essere memorizzato un una directory di backup principale target_dir = '/mnt/e/backup/' # Ricordare di modificare questo in qualcosa che possa essere usato # 3. I file vengono salvati in un file in formato zip # 4. Il nome dell'archivio in formato zip è l'ora e la data corrente target = target_dir + time.strftime('%Y%m%d%H%M%S') + '.zip' # 5. Si usa il comando zip (in Unix/Linux) per salvare i file in un archivio in formato zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Esegue il backup if os.system(zip_command) == 0: print 'Successful backup to', target else: print 'Backup FAILED'
$ python backup_ver1.py Successful backup to /mnt/e/backup/20041208073244.zip
Entriamo quindi nella fase di test per verificare che il programma funzioni correttamente. Se il programma non si comporta come ci si aspetta, occorre fare un debug per rimuovere eventuali bug (errori) dal programma.
Si sarà notato che abbiamo convertito il progetto in codice in una modalità step-by-step (NdT: passo-passo).
Si devono usare i moduli os e time, e quindi
è necessario importarli. Occorre specificare le directory e i file oggetto
del backup nella lista source. La directory dove memorizzare
tutti i file di backup è dichiarata nella variabile target_dir.
Il nome dell'archivio in formato zip che creeremo è composto dalla data e dall'ora
corrente determinata con la funzione time.strftime().
L'archivio avrà anche un'estensione .zip e verrà fisicamente
memorizzato nella directory target_dir.
La funzione time.strftime() riceve un argomento di formato
come quello usato nel programma precedente. L'argomento di formato
%Y verrà poi sostituito dall'anno con il secolo come numero decimale.
La specifica %m verrà sostituita dal mese come numero decimale
tra 01 e 12, e così via. La lista completa degli
argomenti di formato è riportata nel [Python Reference Manual]
fornito insieme alla distribuzione di Python. Da notare che queste specifiche sono simili
(ma non uguali) a quelle usate nell'istruzione print
(usando % seguito dalla tupla).
Il nome del file in formato zip viene creato usando un operatore di addizione che
concatena le stringhe, cioè unisce le due stringhe e ne
restituisce una nuova. Viene poi creata la stringa zip_command
che contiene il comando che si dorà eseguire. Si può controllare se questo comando
funziona a dovere eseguendolo direttamente da shell (terminale Linux o Dos prompt).
Il comando zip che usiamo accetta in ingresso opzioni e parametri.
L'opzione -q viene usata per indicare che il comando zip dovrebbe
funzionare senza fornire output (quietly).
L'opzione -r specifica che il comando zip dovrebbe funzionare
in modalità ricorsiva (recursively) per le
directory, ovvero dovrebbe includere tutte le sottodirectory ed i files presenti
all'interno di ciascuna sottodirectory. Le due opzioni possono essere combinate e
specificate contemporaneamente con -qr. Le opzioni sono seguite dal
nome dell'archivio in formato zip e dalla lista dei file e delle directory oggetto
del backup. La lista source viene poi convertita in stringa
usando il metodo join di cui è già stato visto l'uso.
Al termine viene eseguito il comando vero e proprio usando la
funzione os.system che esegue il comando come se fosse eseguito
dal sistema, ad esempio in una shell. Viene restituito
0 se il comando è stato eseguito correttamente o un numero diverso
in caso di errore.
A seconda dell'esito del comando, viene stampato un messaggio appropriato che indica se il backup ha avuto successo o meno.
E' possibile impostare la lista source e la directory
target con il nome di qualsiasi file e directory, ma in Windows
occorre fare un po' di attenzione. Windows usa il simbolo di backslash
(\) come carattere separatore di directory, mentre Python
lo usa per rappresentare sequenze di escape!
Per questo è; necessario rappresentare il carattere di backslash usando
una sequenza escape o usando stringhe raw. Per esempio:
'C:\\Documents' o r'C:\Documents' ma
non 'C:\Documents';
in quest'ultimo caso, infatti, si userebbe una sequenza di escape non documentata:
\D !
Ora che lo script per il backup funziona, lo si può usare ogni volta che è necessario fare il backup di file importanti. Gli utenti Linux/Unix dovranno usare il metodo eseguibile discusso precedentemente in modo da poter eseguire lo script di backup dovunque. Questa fase viene chiamata operativa, o di deployment del software.
Il programma preso in esame funziona perfettamente ma, solitamente, i primi programmi non funzionano proprio come ci si aspetta. Per esempio, ci potrebbero essere problemi se non si è progettato il programma in modo corretto o se abbiamo fatto errori di digitazione nel codice, ecc... Ecco perché spesso è necessario tornare alla fase di progettazione o eseguire un debug del programma.
Sebbene la prima versione dello script funziona a dovere, vogliamo apportare alcune rifiniture in modo che possa funzionare meglio su base giornaliera. Questa fase è chiamata manutenzione del software.
Una delle rifiniture da apportare è un miglior meccanismo per la nomenclatura del file che usi l'ora come base per il nome del file, lo memorizzi in una directory avente per nome la data corrente e che sia interna alla directory principale di backup. Il vantaggio è che il backup verrà memorizzato in modo gerarchico e perciò sarà più facile da gestire. Inoltre il nome dei file risulterà più corto, le directory separate facilitano il controllo nel caso di backup programmati su base giornaliera, ecc...
Esempio 10.2. Script di backup - Seconda versione
#!/usr/bin/python # Filename: backup_ver2.py import os import time # 1. I file e le directory che sono oggetto del backup vengono specificate in una lista source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Se si sta usando Windows, usare source = [r'C:\Documents', r'D:\Work'] o qualcosa di simile # 2. Il backup deve essere memorizzato un una directory di backup principale target_dir = '/mnt/e/backup/' # Ricordare di modificare questo in qualcosa che possa essere usato # 3. I file vengono salvati in un file in formato zip # 4. Il giorno corrente è il nome della sottodirectory nella directory principale today = target_dir + time.strftime('%Y%m%d') # L'ora corrente è il nome dell'archivio in formato zip now = time.strftime('%H%M%S') # Crea la sottodirectory se non esiste if not os.path.exists(today): os.mkdir(today) # crea la directory print 'Successfully created directory', today # Il nome dell'archivio in formato zip target = today + os.sep + now + '.zip' # 5. Si usa il comando zip (in Unix/Linux) per salvare i file in un archivio in formato zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Esegue il backup if os.system(zip_command) == 0: print 'Successful backup to', target else: print 'Backup FAILED'
$ python backup_ver2.py Successfully created directory /mnt/e/backup/20041208 Successful backup to /mnt/e/backup/20041208/080020.zip $ python backup_ver2.py Successful backup to /mnt/e/backup/20041208/080428.zip
Buona parte del programma è rimasta la stessa. Le modifiche riguardano
il controllo sull'esistenza di una directory avente per nome la data corrente
e posta all'interno della directory principale di backup; tale controllo viene
fatto con la funzione os.exists. Se la directory non
esiste, essa viene creata usando la funzione os.mkdir.
Da notare l'uso della variabile os.sep, la quale rappresenta
il carattere di separazione delle directory in accordo col sistema operativo in uso,
che sarà '/' in Linux/Unix, '\\' in
Windows e ':' in Mac OS. L'uso di os.sep
invece dei caratteri letterali rende il programma portabile e funzionante su
tutti i sistemi.
La seconda versione funziona perfettamente quando si fanno molti backup, ma resta il problema della determinazione del loro contenuto. Per esempio, si supponga di aver apportato delle modifiche ad un programma o ad una presentazione e di voler associare la tipologia delle stesse al nome dell'archivio salvato in formato zip. Questo risultato può essere ottenuto facilmente allegando un breve commento fornito dall'utente al nome dell'archivio zip.
Esempio 10.3. Script di backup - Terza versione (non funzionante!)
#!/usr/bin/python # Filename: backup_ver2.py import os import time # 1. I file e le directory che sono oggetto del backup vengono specificate in una lista source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Se si sta usando Windows, usare source = [r'C:\Documents', r'D:\Work'] o qualcosa di simile a questo # 2. Il backup deve essere memorizzato un una directory di backup principale target_dir = '/mnt/e/backup/' # Ricordare di modificare questo in qualcosa che possa essere usato # 3. I file vengono salvati in un file in formato zip # 4. Il giorno corrente è il nome della sottodirectory nella directory principale today = target_dir + time.strftime('%Y%m%d') # L'ora corrente è il nome dell'archivio in formato zip now = time.strftime('%H%M%S') # Accetta un commento dell'utente per creare il nome dell'archivio in formato zip comment = raw_input('Enter a comment --> ') if len(comment) == 0: # controlla se è stato immesso un commento target = today + os.sep + now + '.zip' else: target = today + os.sep + now + '_' + comment.replace(' ', '_') + '.zip' # Crea la sottodirectory se non esiste if not os.path.exists(today): os.mkdir(today) # crea la directory print 'Successfully created directory', today # 5. Si usa il comando zip (in Unix/Linux) per salvare i file in un archivio in formato zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Esegue il backup if os.system(zip_command) == 0: print 'Successful backup to', target else: print 'Backup FAILED'
$ python backup_ver3.py File "backup_ver3.py", line 25 target = today + os.sep + now + '_' + ^ SyntaxError: invalid syntax
Questo programma non funziona!. Python dice che c'è un errore, cioè lo script non soddisfa la struttura che Python si aspetta di trovare. Osservando l'errore emesso da Python, si nota che viene riportato il punto in cui è stato scoperto! Quindi è necessario fare un debug del programma a partire da questa riga.
Da un'attenta osservazione si nota che la linea logica è stata
divisa in due linee fisiche senza specificare che queste linee fisiche
fanno parte della stessa linea logica. In questa linea logica Python
trova infatti l'operatore di addizione (+) senza
nessun operando, e perciò non sa come continuare. Per poter usare una
linea logica scritta su più linee fisiche si deve usare il
carattere di backslash alla fine di ogni linea fisica incompleta.
Con questo in mente possiamo correggere il programma. Questa fase è
chiamata correzione degli errori
(bug fixing).
Esempio 10.4. Script di backup - Quarta versione
#!/usr/bin/python # Filename: backup_ver2.py import os, time # 1. I file e le directory che sono oggetto del backup vengono specificate in una lista source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Se si sta usando Windows, usare source = [r'C:\Documents', r'D:\Work'] o qualcosa di simile a questo # 2. Il backup deve essere memorizzato un una directory di backup principale target_dir = '/mnt/e/backup/' # Ricordare di modificare questo in qualcosa che possa essere usato # 3. I file vengono salvati in un file in formato zip # 4. Il giorno corrente è il nome della sottodirectory nella directory principale today = target_dir + time.strftime('%Y%m%d') # L'ora corrente è il nome dell'archivio in formato zip now = time.strftime('%H%M%S') # Accetta un commento dell'utente per creare il nome dell'archivio in formato zip comment = raw_input('Enter a comment --> ') if len(comment) == 0: # controlla se è stato immesso un commento target = today + os.sep + now + '.zip' else: target = today + os.sep + now + '_' + \ comment.replace(' ', '_') + '.zip' # Notice the backslash! # Crea la sottodirectory se non esiste if not os.path.exists(today): os.mkdir(today) # crea la directory print 'Successfully created directory', today # 5. Si usa il comando zip (in Unix/Linux) per salvare i file in un archivio in formato zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Esegue il backup if os.system(zip_command) == 0: print 'Successful backup to', target else: print 'Backup FAILED'
$ python backup_ver4.py Enter a comment --> added new examples Successful backup to /mnt/e/backup/20041208/082156_added_new_examples.zip $ python backup_ver4.py Enter a comment --> Successful backup to /mnt/e/backup/20041208/082316.zip
Il programma ora funziona e permette di valutare le migliorie fatte
alla versione tre. I commenti dell'utente si estraggono usando la
funzione raw_input e si controlla se l'utente
ha effettivamente annotato qualcosa valutando la lunghezza dell'input
con la funzione len. Se l'utente ha premuto solo
il tasto enter per qualche ragione (era solo un backup
routinario o non sono state fatte modifiche speciali), il programma
procede come nel caso della versione precedente.
Se è stato fornito un commento, questo viene attaccato al nome
dell'archivio zip prima dell'estensione .zip.
Si noti che sono stati sostituiti tutti gli spazi presenti nel commento
con caratteri di sottolineatura per semplificarne la gestione.
La quarta versione è uno script funzionante e soddisfacente per la maggior parte
degli utenti, ma c'è sempre posto per le migliorie. Ad esempio, si può includere
un livello di verbosità al programma potendo specificare
un'opzione -v .
Un'altra miglioria possibile potrebbe essere la possibilità di passare
file e directory extra allo script da linea di comando. Si potranno ottenere
dalla lista sys.argv e quindi aggiungerli alla lista
source con il metodo extend
fornito dalla classe list .
Una miglioria molto comoda è l'uso del comando tar al posto
del comando zip, che in combinazione con gzip,
permette una creazione più rapida e compatta del file di backup.
In Windows questo archivio è gestito dall'applicazione
WinZip che riconosce i files .tar.gz .
Il comando tar è disponibile di default sulla quasi totalità
dei sistemi Linux/Unix. Gli utenti Windows possono
scaricarlo
ed installarlo.
La stringa di comando diverrà come segue:
tar = 'tar -cvzf %s %s -X /home/swaroop/excludes.txt' % (target, ' '.join(srcdir))
Le opzioni vengono spiegate di seguito.
-c indica la creazione di un archivio.
-v indica la verbosità,
il comando dovrebbe essere più 'prolisso'.
-z indica che deve essere usato il filtro
gzip.
-f indica la
forzatura nella creazione dell'archivio,
eventualmente sostituendo un file esistente con lo stesso nome.
-X indica un file che contiene una lista di nomi di file
che devono essere esclusi (excluded)
dal backup. Per esempio, specificando *~ nel backup non
verrà incluso nessun file che termina con ~.
Il modo migliore per creare questo tipi di archivi richiede l'uso dei moduli
zipfile o tarfile. Essi fanno parte
della libreria standard di Python e sono disponibili all'uso. Usando queste
librerie si evita la necessità di ricorrere a os.system,
il cui uso facilmente porta ad introdurre degli errori.
Nonostante ciò nell'esempio si ricorre a os.system
a scopo puramente illustrativo e in modo tale che l'esempio risultasse comprensibile
da tutti ed abbastanza reale da essere utile.