Un cop disposem d'un disseny del nostre programa, podem escriure el codi que serà la implementació de la nostra solució.
Example 10.1. Script de còpies de seguretat - La primera versió
#!/usr/bin/python # Filename: backup_ver1.py import os import time # 1. Els fitxers i els directoris que han de ser guardats, s'especificaran a una llista. source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Des de Windows fer servir source = [r'C:\Documents', r'D:\Work'] o similar # 2. La còpia de seguretat ha de ser guardada en un directori principal de còpies de seguretat. target_dir = '/mnt/e/backup/' # Canviar aquest pel lloc que fem servir # 3. Els fitxers es guarden en un fitxer zip. # 4. El nom de l'arxiu zip és la data i hora actual. target = target_dir + time.strftime('%Y%m%d%H%M%S') + '.zip' # 5. Fem servir la comanda zip (Unix/Linux) per a col·locar els fitxers a un arxiu zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Executem la còpia 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
Ara passem a la fase de prova on comprovem el correcte funcionament del nostre programa. Si no es comporta com s'esperava, haurem de depurar-lo, és a dir, eliminar els errors o bugs del programa.
Podem veure com hem convertit pas a pas, el nostre disseny en codi.
Primer importem els mòduls os
i time
.
A continuació especifiquem a la llista source
els
fitxers i directoris a copiar. El directori objectiu és on guardarem totes
les còpies de seguretat i s'especifica a la variable
target_dir
.
El nom del fitxer zip que crearem és la data i hora actual, que obtenim
a partir de la funció time.strftime()
.
També especifiquem l'extensió .zip
i què serà
col·locat al directori target_dir
.
La funció time.strftime()
pren com a paràmetre el
format de la data i hora. L'especificació %Y
serà substituida per l'any (en dos dígits). %m
prendrà
per valor el mes en forma de número entre el 01
i
el 12
. I així amb la resta.
La llista completa d'especificacions pot trobar-se al manual de referència
de Python ([Python Reference Manual]) dins la
distribució de Python.
Podem observar que és similar, malgrat no igual, a l'especificació de la
instrucció print
. Ambdós fan servir
%
seguit per una tupla.
Creem el nom de fitxer zip fent servir l'operador de concatenació de
strings.
A continuació creem el string zip_command
què conté
la comanda que executarem. Es pot comprovar que la comanda funciona,
executant-la a l'intèrpret d'ordres (terminal Linux o DOS).
La comanda zip que fem servir té algunes opcions i
paràmetres. L'opció -q
indica que la comanda ha de
funcionar en mode quiet (silenciós).
L'opció -r
indica que ha de funcionar de manera
recursiva (és a dir, ha d'incloure
també els fitxers dels subdirectoris).
Les dues opcions han estat combinades de manera breu amb
-qr
. A continuació s'afegeix el nom de l'arxiu a crear
seguit per la llista de fitxers a copiar.
Convertim la llista source
en un string amb el mètode
join
.
Finalment executem la comanda fent servir la funció
os.system
que l'executa com si l'haguèssim introduit
a l'interpret d'ordres. Aquesta funció retorna 0
quan
l'execució ha tingut èxit, o un un número d'error altrament.
Depenent de la sortida de la comanda, informem l'usuari de si la còpia de seguretat ha tingut o no èxit. I això és tot: acabem de crear un script que fa còpies de seguretat dels nostres fitxers importants!
Podem assignar a la llista source
i a la variable
target
a qualsevol fitxer i directori, però hem
d'anar amb una mica de compte en Windows. El problema és què Windows
fa servir la contrabarra (\
) com a caràcter
separador de directoris però Python fa servir aquest caràcter per a
representar seqüències d'escapament.
Així, ens caldrà representar la contrabarra fent servir una seqüència
d'escapament o bé fer servir strings crus (no processats). Per exemple,
'C:\\Documents'
o r'C:\Documents'
però mai farem servir
'C:\Documents'
- o estaríem especificant la
seqüència d'escapament desconeguda \D
!
Ara que tenim el nostre programa en funcionament, podem fer-lo servir sempre que vulguem per a fer còpies de seguretat. Es recomana als usuaris de Linux/unix que facin servir el mètode d'execució discutit anteriorment, de manera que es pugui executar el script en qualsevol moment i des de qualsevol lloc. Aquesta fase es coneix com a fase d'operació.
El programa anterior funciona correctament, però normalment els primers programes no funcionen com s'esperava. Per exemple, poden aparèixer problemes si no l'hem dissenyat adequadament, o si hem fet algun error a l'hora de teclejar el codi. En aquests casos, ens caldrà tornar enrere a la fase de disseny o a la d'implementació, i depurar el programa.
La primera versió del nostre script funciona. Amb tot, podem fer-li alguns canvis de manera que funcioni millor per l'ús diari. D'això se'n diu la fase de manteniment del programari.
Un dels canvis que podrien ser útils seria un millor mecanisme d'anomenar els fitxers - fent servir l'hora (time) com a nom del fitxer dins un directori amb la data actual (date) com a directori dins del directori principal de les còpies de seguretat. Un dels avantatges és que les nostres còpies serien emmagatzemades en forma jeràrquica, el que faria més fàcil la seva gestió. Un altre avantatge estaria en que la longitud dels noms dels fitxers seria molt menor. Encara un altre avantatge és que els directoris separats ofereixen una manera més ràpida de comprovar que s'ha realitzat una còpia cada dia donat que un directori només es crearà quan s'hagi fet una còpia de seguretat d'aquell dia.
Example 10.2. Script de còpies de seguretat - La segona versió
#!/usr/bin/python # Filename: backup_ver2.py import os import time # 1. Els fitxers i els directoris que han de ser guardats, s'especificaran a una llista. source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Des de Windows fer servir source = [r'C:\Documents', r'D:\Work'] o similar # 2. La còpia de seguretat ha de ser guardada en un directori principal de còpies de seguretat. target_dir = '/mnt/e/backup/' # Canviar aquest pel lloc que fem servir # 3. Els fitxers es guarden en un fitxer zip. # 4. El dia actual és el nom del subdirectori al directori principal today = target_dir + time.strftime('%Y%m%d') # L'hora actual és el nom de l'arxiu zip now = time.strftime('%H%M%S') # Crea el subdirectori si encara no existeix if not os.path.exists(today): os.mkdir(today) # make directory print 'Successfully created directory', today # El nom de l'arxiu zip target = today + os.sep + now + '.zip' # 5. Fem servir la comanda zip (Unix/Linux) per a col·locar els fitxers a un arxiu zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Executem la còpia 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
La major part del programa no ha canviat. El canvi principal és que ara
comprovem si hi ha un directori amb la data d'avui com a nom dins del directori
principal de les còpies de seguretat. Per a això, fem servir la funció
os.exists
. Si no existeix, el creem amb la funció
os.mkdir
.
Fixem-nos com fem servir la variable os.sep
. Aquesta ens
ofereix el separador de directoris corresponent al sistema operatiu que fem
servir. És a dir, tindrà com a valor
'/'
a Linux/Unix, '\\'
a Windows i ':'
a Mac OS.
L'ús de
os.sep
en comptes dels caràcters directament, fa que el nostre
programa sigui més portable i funcione sobre aquests sistemes.
La segona versió va bé per fer algunes còpies de seguretat. Però, quan n'hi ha moltes, trobo difícil diferenciar els objectius de les diferents còpies de seguretat. Per exemple, podria haver fet alguns canvis importants a un programa o presentació, i voler associar aquests canvis al nom de l'arxiu zip. Això es pot aconseguir fàcilment adjuntant un comentari de l'usuari al nom de l'arxiu zip.
Example 10.3. Script de còpies de seguretat - La tercera versió (no funciona!)
#!/usr/bin/python # Filename: backup_ver2.py import os import time # 1. Els fitxers i els directoris que han de ser guardats, s'especificaran a una llista. source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Des de Windows fer servir source = [r'C:\Documents', r'D:\Work'] o similar # 2. La còpia de seguretat ha de ser guardada en un directori principal de còpies de seguretat. target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using # 3. Els fitxers es guarden en un fitxer zip. # 4. El dia actual és el nom del subdirectori al directori principal today = target_dir + time.strftime('%Y%m%d') # L'hora actual és el nom de l'arxiu zip now = time.strftime('%H%M%S') # Obté el comentari de l'usuari per a crear el nom del fitxer comment = raw_input('Enter a comment --> ') if len(comment) == 0: # comprova que hagi estat introduït un comentari target = today + os.sep + now + '.zip' else: target = today + os.sep + now + '_' + comment.replace(' ', '_') + '.zip' # Crea el subdirectori si encara no existeix if not os.path.exists(today): os.mkdir(today) # make directory print 'Successfully created directory', today # 5. Fem servir la comanda zip (Unix/Linux) per a col·locar els fitxers a un arxiu zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Executem la còpia 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
Aquest programa no funciona!. Python ens diu que hi ha un error sintàctic que vol dir que el programa no satisfà l'estructura que Python espera trobar. Si observem el missatge d'error que ens dóna, també ens informa on s'ha detectat l'error. Així comencem a depurar el programa des d'aquesta línia.
Fixant-nos amb atenció, trobem que la línia lògica s'ha dividit
en dues línies físiques, però no havíem dit enlloc que aquestes
dues línies haguessin d'estar juntes. Bàsicament, Python ha trobat
l'operador d'addició (+
) sense cap operand a
aquesta línia lògica, i per tant, no sap com continuar.
Recordem que podem indicar que una línia lògica continua a la següent
línia física amb la contrabarra al final de la primera línia física.
Corregim l'error del nostre programa.
Example 10.4. Script de còpies de seguretat - La quarta versió
#!/usr/bin/python # Filename: backup_ver2.py import os, time # 1. Els fitxers i els directoris que han de ser guardats, s'especificaran a una llista. source = ['/home/swaroop/byte', '/home/swaroop/bin'] # Des de Windows fer servir source = [r'C:\Documents', r'D:\Work'] o similar # 2. La còpia de seguretat ha de ser guardada en un directori principal de còpies de seguretat. target_dir = '/mnt/e/backup/' # Remember to change this to what you will be using # 3. Els fitxers es guarden en un fitxer zip. # 4. El dia actual és el nom del subdirectori al directori principal today = target_dir + time.strftime('%Y%m%d') # L'hora actual és el nom de l'arxiu zip now = time.strftime('%H%M%S') # Obté el comentari de l'usuari per a crear el nom del fitxer comment = raw_input('Enter a comment --> ') if len(comment) == 0: # comprova que hagi estat introduït un comentari target = today + os.sep + now + '.zip' else: target = today + os.sep + now + '_' + \ comment.replace(' ', '_') + '.zip' # Notice the backslash! # Crea el subdirectori si encara no existeix if not os.path.exists(today): os.mkdir(today) # make directory print 'Successfully created directory', today # 5. Fem servir la comanda zip (Unix/Linux) per a col·locar els fitxers a un arxiu zip zip_command = "zip -qr '%s' %s" % (target, ' '.join(source)) # Executem la còpia 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
Aquest programa funciona. Analitzem les millores que l'hem incorporat
respecte a la versió 3.
Obtenim el comentari de l'usuari amb la funció raw_input
i a continuació comprovem si l'usuari realment ha introduït alguna cosa,
comprovant la longitud de l'entrada amb la funció len
.
Cas que l'usuari simplement hagi pres la tecla enter
(potser simplement es tracta d'una còpia de seguretat rutinària que
no correspon a cap canvi especial), llavors procedim com ho fèiem abans.
En canvi, si s'ha introduït un comentari, afegim aquest al nom de l'arxiu
zip tot just abans de l'extensió .zip
.
Notem que canviem els espais per guions baixos per a simplificar la
manipulació d'aquests noms.
La quarta versió és un script que funciona satisfactòriament per a la majoria
d'usuaris. Però sempre hi ha lloc per a fer millores. Per exemple, podem incloure
l'opció -v
per fer que el programa generi més informació durant
el seu funcionament.
Una altra millora pot ser permetre recollir fitxers i directoris des de la
línia de comandes. Aquests es poden obtenir de la llista sys.argv
,
i després passar-los a la nostra llista source
mitjançant el mètode
extend
que ens proporciona la classe list
.
Una millora que m'agrada és l'ús de la comanda tar en comptes
de zip. Quan fem servir tar juntament amb
gzip, el procés sol ser més ràpid i l'arxiu resultant molt menor.
L'aplicació WinZip és capaç de manegar els fitxers
.tar.gz
des de Windows.
La comanda tar es troba disponible per defecte a la majoria
de les instal·lacions Linux/Unix. Els usuaris de Windows poden descarregar-la
des de
http://gnuwin32.sourceforge.net/packages/tar.htm.
La línia de comanda serà ara:
tar = 'tar -cvzf %s %s -X /home/swaroop/excludes.txt' % (target, ' '.join(srcdir))
Les opcions s'expliquen a continuació.
-c
indica creació
d'un arxiu.
-v
indica verbose,
és a dir, fer que el programa generi més informació.
-z
indica que es faci servir el filtre gzip.
-f
indica forçar
la creació de l'arxiu. És a dir, en cas que existeixi un fitxer amb el mateix
nom, serà substituït pel nou.
-X
indica un fitxer que conté una llista de fitxers
que han de ser exclosos de la còpia.
Per exemple, podem especificar *~
en aquest fitxer
de manera que no s'inclogui a la còpia cap fitxer que finalitzi
amb ~
.
La manera preferida de crear aquesta classe d'arxius seria fent
servir els mòduls zipfile
o tarfile
.
Aquests mòduls formen part de la biblioteca estàndard de Python i estan
disponibles per ser utilitzats. L'ús d'aquestes biblioteques ens permet
evitar les crides a os.system
, cosa generalment recomanable
per l'alt risc a cometre errors costosos.
Hem fet servir os.system
per a crear les còpies de
seguretat només amb un propòsit pedagògic, fent que l'exemple fos
prou senzill d'entendre a l'hora que prou real per a ser útil.