A Byte of Python

Die Lösung

Nachdem der Entwurf unseres Programms nun feststeht, können wir den Code schreiben, der eine Implementierung unserer Lösung darstellt.

Die erste Version

Beispiel 10.1. Sicherungsskript - erste Version (sicherung_ver1.py)

				
#!/usr/bin/python

import os
import time

# 1. Die Dateien und Verzeichnisse, die gesichert werden sollen,
# werden in der folgenden Liste angegeben:
quellen = ['/home/swaroop/byte', '/home/swaroop/bin']
# Unter Windows muessen Sie die Pfade auf diese Weise angeben:
# quellen = ['C:\\Dokumente', 'D:\\Arbeit']

# 2. Die Sicherung muss in das folgende Hauptverzeichnis fuer
# Sicherungen gespeichert werden:
ziel_verzeichnis = '/mnt/e/sicherung/'
# Denken Sie daran, dies an Ihre Gegebenheiten anzupassen.

# 3. Die Dateien werden in einer ZIP-Datei gesichert.

# 4. Der Name der ZIP-Datei setzt sich aus dem aktuellen Datum
# und der Uhrzeit wie folgt zusammen:
ziel = ziel_verzeichnis + time.strftime('%Y%m%d%H%M%S') + '.zip'

# 5. Wir benutzen den Befehl zip (unter Unix/Linux), um die Dateien
# zu einem ZIP-Archiv zu komprimieren:
zip_befehl = 'zip -qr %s %s' % (ziel, ' '.join(quellen))
# Windows-Benutzer koennen z.B. PKZIP oder Info-ZIP in das
# Windows-Systemverzeichnis kopieren, damit dies funktioniert.

# Sicherung starten
if os.system(zip_befehl) == 0:
	print 'Erfolgreiche Sicherung nach', ziel
else:
	print 'Sicherung fehlgeschlagen!'
				
				

Ausgabe

				
$ python sicherung_ver1.py
Erfolgreiche Sicherung nach /mnt/e/backup/20041208073244.zip
				
				

Wir sind nun in der Test-Phase, in der wir ausprobieren, ob unser Programm ordentlich funktioniert. Wenn es sich nicht so verhält, wie erwartet, dann müssen wir unser Programm debuggen, d.h. die Bugs (Fehler) aus dem Programm entfernen.

So funktioniert es

Sie werden bemerkt haben, wie wir unseren Entwurf Schritt für Schritt in Programm-Code umgewandelt haben.

Wir benutzen die Module os und time, weswegen wir sie am Anfang importieren. Danach geben wir in einer Liste namens Quellen die Quelldateien und -verzeichnisse an, die gesichert werden sollen. Das Zielverzeichnis, in das wir die Sicherungsdateien speichern, wird in der Variable ziel_verzeichnis angegeben. Der Name des ZIP-Archivs, das wir erzeugen werden, setzt sich aus dem aktuellen Datum und der Uhrzeit zusammen, die wir mit der Funktion time.strftime() ermitteln. Dem Namen wird noch die Dateiendung .zip angehängt und das Zielverzeichnis vorangestellt. Dieser vollständige Dateiname mit Pfad wird in der Variable ziel gespeichert.

Der Funktion time.strftime() muss eine Formatspezifikation übergeben werden, wie wir sie im obigen Programm benutzt haben. Das %Y wird hierbei durch das Jahr ersetzt. Das %m wird durch den Monat als Dezimalzahl zwischen 01 und 12 ersetzt, und so weiter. Die vollständige Liste aller solcher Format-Spezifikationen kann im [Python-Referenz-Handbuch] nachgelesen werden, das zusammen mit Python ausgeliefert wird. Beachten Sie, dass dies der Spezifikation ähnelt (aber nicht das gleiche ist), wie wir sie im print-Befehl benutzt haben (mit dem %-Zeichen, gefolgt von einem Tupel).

Wir erzeugen den Namen der Ziel-ZIP-Datei, indem wir den Summierungs-Operator (das Plus-Zeichen) benutzen, der die einzelnen Strings miteinander verkettet, d.h. er verbindet zwei Strings und liefert dies als neuen String zurück. Danach erzeugen wir einen String zip_befehl, der den Befehl enthält, den wir ausführen wollen. Sie können ausprobieren, ob dieser Befehl funktioniert, indem Sie ihn auf der Kommandozeilen-Ebene ausprobieren (Linux-Terminal oder DOS-Eingabeaufforderung).

Dem von uns verwendeten Befehl zip geben wir noch einige Optionen und Parameter mit. Die Option -q wird benutzt, um anzuzeigen, dass der zip-Befehl ohne Ausgaben arbeiten soll (d.h. ohne viel bei der Arbeit zu quatschen). Die Option -r gibt an, dass der zip-Befehl rekursiv arbeiten soll, d.h. er soll die Unterverzeichnisse und Dateien in den Unterverzeichnissen mit archivieren. Die beiden Optionen werden kombiniert in der Kurzform -qr angegeben. Den Optionen folgt der Name des zu erzeugenden ZIP-Archivs und die Liste der Dateien und Verzeichnisse, die in dem Archiv gesichert werden sollen. Wir konvertieren die Liste quellen in einen String, indem wir die join-Methode für Strings benutzen, die wir bereits früher kennen gelernt haben.

Danach starten wir endlich den Befehl, indem wir die Funktion os.system benutzen, die einen Befehl auf Betriebssystemebene des Systems ausführt, d.h. in einer Kommandozeilen-Umgebung - wobei eine 0 zurückgegeben wird, wenn der Befehl erfolgreich ausgeführt wurde, oder einen Fehlercode ungleich 0 im Fall, dass ein Fehler aufgetreten ist.

Je nachdem, wie die Ausführung des Befehls ausgegangen ist, geben wir eine entsprechende Meldung auf dem Bildschirm aus, dass die Sicherung fehlgeschlagen ist oder erfolgreich war, und das war's, wir haben damit ein Skript erzeugt, das eine Sicherungskopie unserer wichtigen Dateien erzeugt!

Anmerkung für Windows-Benutzer

Sie können in der Liste quellen und der Variable ziel_verzeichnis beliebige Datei- bzw. Verzeichnisnamen angeben, aber Sie müssen unter Windows ein wenig Acht geben. Das Problem ist, dass Windows den umgekehrten Schrägstrich (\) als das Trennzeichen für Verzeichnisse benutzt, Python jedoch den umgekehrten Schrägstrich für Maskierungscodes benutzt!

Daher müssen Sie einen umgekehrten Schrägstrich selber durch einen Maskierungscode oder durch die Benutzung von rohen Zeichenketten darstellen. Zum Beispiel können Sie 'C:\\Dokumente' oder r'C:\Dokumente' schreiben, nicht aber 'C:\Dokumente' - Sie verwenden hier einen unbekannten Maskierungscode \D!

Nachdem wir nun ein funktionierendes Sicherungsskript haben, können wir es jederzeit benutzen, um eine Sicherungskopie der Dateien zu erstellen. Für Linux/Unix-Benutzer empfiehlt es sich, die früher besprochene Methode für ausführbare Dateien zu verwenden, damit das Sicherungsskript jederzeit und überall ausgeführt werden kann. Dies wird die Produktions- oder Einsatz-Phase der Software genannt.

Das obige Programm funktioniert zufrieden stellend, aber (meistens) funktionieren erste Versionen von Programmen nicht genau so, wie man es erwartet. Zum Beispiel könnten Probleme entstehen, wenn das Programm nicht sauber entworfen wurde, oder wenn man einen Fehler beim Eintippen des Programmcodes gemacht hat, usw. Dementsprechend muss man dann wieder in die Entwurfsphase zurückgehen oder sein Programm debuggen.

Die zweite Version

Die erste Version unseres Skripts funktioniert. Wir können aber noch einige Verfeinerungen daran vornehmen, so dass es für den täglichen Einsatz besser tauglich wird. Man nennt dies die Wartungs-Phase der Software.

Eine der Verfeinerungen, die ich nützlich fand, ist ein besserer Mechanismus zur Benennung der Sicherungsdateien - indem die Uhrzeit als der Name der Datei verwendet wird, die in einem Verzeichnis gespeichert wird, dessen Name aus dem aktuellen Datum gebildet wird, und das sich im Hauptverzeichnis für die Sicherungen befindet. Ein Vorteil davon ist, dass die Sicherungen in einer hierarchischen Weise gespeichert werden und daher einfacher zu verwalten sind. Ein weiterer Vorteil ist, dass die Länge der Dateinamen auf diese Weise viel kürzer wird. Noch ein weiterer Vorteil ist, dass verschiedene Verzeichnisse dabei helfen, auf einfache Weise zu überprüfen, ob man an jedem Tag eine Sicherung gemacht hat, denn das Verzeichnis wird nur angelegt, wenn am jeweiligen Tag eine Sicherung vorgenommen wurde.

Beispiel 10.2. Sicherungsskript - zweite Version (sicherung_ver2.py)

				
#!/usr/bin/python

import os
import time

# 1. Die Dateien und Verzeichnisse, die gesichert werden sollen,
# werden in der folgenden Liste angegeben:
quellen = ['/home/swaroop/byte', '/home/swaroop/bin']
# Unter Windows muessen Sie die Pfade auf diese Weise angeben:
# quellen = ['C:\\Dokumente', 'D:\\Arbeit']

# 2. Die Sicherung muss in das folgende Hauptverzeichnis fuer
# Sicherungen gespeichert werden:
ziel_verzeichnis = '/mnt/e/sicherung/'
# Denken Sie daran, dies an Ihre Gegebenheiten anzupassen.

# 3. Die Dateien werden in einer ZIP-Datei gesichert.

# 4. Das Tagesdatum ist der Name des Unterverzeichnisses:
heute = ziel_verzeichnis + time.strftime('%Y%m%d')
# Die aktuelle Uhrzeit ist der Name des ZIP-Archivs:
jetzt = time.strftime('%H%M%S')

# Erzeuge das Unterverzeichnis, wenn noch nicht vorhanden:
if not os.path.exists(heute):
	os.mkdir(heute) # erzeuge das Verzeichnis
	print 'Verzeichnis', heute, 'erfolgreich angelegt'

# Der Name der ZIP-Datei:
ziel = heute + os.sep + jetzt + '.zip'

# 5. Wir benutzen den Befehl zip (unter Unix/Linux), um die Dateien
# zu einem ZIP-Archiv zu komprimieren:
zip_befehl = 'zip -qr %s %s' % (ziel, ' '.join(quellen))
# Windows-Benutzer koennen z.B. PKZIP oder Info-ZIP in das
# Windows-Systemverzeichnis kopieren, damit dies funktioniert.

# Sicherung starten
if os.system(zip_befehl) == 0:
	print 'Erfolgreiche Sicherung nach', ziel
else:
	print 'Sicherung fehlgeschlagen!'
				
				

Ausgabe

				
$ python sicherung_ver2.py
Verzeichnis /mnt/e/backup/20041208 erfolgreich angelegt
Erfolgreiche Sicherung nach /mnt/e/backup/20041208/080020.zip

$ python sicherung_ver2.py
Erfolgreiche Sicherung nach /mnt/e/backup/20041208/080428.zip
				
				

So funktioniert es

Das Programm bleibt im Wesentlichen das gleiche. Die Änderungen bestehen darin, dass wir prüfen, of es bereits ein Verzeichnis mit dem aktuellen Tagesdatum als Namen innerhalb des Hauptverzeichnisses für Sicherungen gibt, wofür wir die Funktion os.exists einsetzen. Wenn es nicht existiert, legen wir das Verzeichnis mit der Funktion os.mkdir an.

Beachten Sie die Benutzung der Variablen os.sep - sie stellt das Trennzeichen für Verzeichnisse des jeweils benutzten Betriebssystems dar, d.h. sie wird unter Unix oder Linux '/' sein, unter Windows wird sie '\\' sein, und ':' unter MacOS. Indem man os.sep verwendet, anstatt diese Zeichen direkt hinzuschreiben, wird das Programm portabel und funktioniert plattformunabhängig unter all diesen Betriebssystemen.

Die dritte Version

Die zweite Version funktioniert hervorragend, wenn man viele Sicherungskopien macht, aber wenn man so viele Sicherheitskopien macht, dann wird es schwer zu unterscheiden, wofür diese Sicherungskopien gemacht worden sind. Zum Beispiel könnte ich einige größere Änderungen an einem Programm oder einer Präsentation gemacht haben, und möchte dann, dass diese Änderungen mit dem Namen des ZIP-Archivs verknüpft werden. Dies kann einfach dadurch erreicht werden, dass man dem Namen des ZIP-Archivs einen benutzerdefinierten Kommentar anhängt.

Beispiel 10.3. Sicherungsskript - dritte Version (funktioniert nicht!) (sicherung_ver3.py)

				
#!/usr/bin/python

import os
import time

# 1. Die Dateien und Verzeichnisse, die gesichert werden sollen,
# werden in der folgenden Liste angegeben:
quellen = ['/home/swaroop/byte', '/home/swaroop/bin']
# Unter Windows muessen Sie die Pfade auf diese Weise angeben:
# quellen = ['C:\\Dokumente', 'D:\\Arbeit']

# 2. Die Sicherung muss in das folgende Hauptverzeichnis fuer
# Sicherungen gespeichert werden:
ziel_verzeichnis = '/mnt/e/sicherung/'
# Denken Sie daran, dies an Ihre Gegebenheiten anzupassen.

# 3. Die Dateien werden in einer ZIP-Datei gesichert.

# 4. Das Tagesdatum ist der Name des Unterverzeichnisses:
heute = ziel_verzeichnis + time.strftime('%Y%m%d')
# Die aktuelle Uhrzeit ist der Name des ZIP-Archivs:
jetzt = time.strftime('%H%M%S')

# Eine Anmerkung als Benutzereingabe entgegennehmen,
# die fuer den Namen der ZIP-Datei verwendet wird:
anmerkung = raw_input('Geben Sie eine Anmerkung ein --> ')
if len(anmerkung) == 0: # pruefe, ob eine Anmerkung eingegeben wurde
	ziel = heute + os.sep + jetzt + '.zip'
else:
	ziel = heute + os.sep + jetzt + '_' +
		anmerkung.replace(' ', '_') + '.zip'

# Erzeuge das Unterverzeichnis, wenn noch nicht vorhanden:
if not os.path.exists(heute):
	os.mkdir(heute) # erzeuge das Verzeichnis
	print 'Verzeichnis', heute, 'erfolgreich angelegt'

# 5. Wir benutzen den Befehl zip (unter Unix/Linux), um die Dateien
# zu einem ZIP-Archiv zu komprimieren:
zip_befehl = 'zip -qr %s %s' % (ziel, ' '.join(quellen))
# Windows-Benutzer koennen z.B. PKZIP oder Info-ZIP in das
# Windows-Systemverzeichnis kopieren, damit dies funktioniert.

# Sicherung starten
if os.system(zip_befehl) == 0:
	print 'Erfolgreiche Sicherung nach', ziel
else:
	print 'Sicherung fehlgeschlagen!'
				
				

Ausgabe

				
$ python sicherung_ver3.py
  File "sicherung_ver3.py", line 30
    ziel = heute + os.sep + jetzt + '_' +
                                        ^
SyntaxError: invalid syntax
					
				

Warum dies nicht funktioniert

Dieses Programm funktioniert nicht! Python sagt, dass es einen Syntax-Fehler hat, was bedeutet, dass das Skript an einer Stelle nicht die Struktur aufweist, die Python hier erwartet. Wenn wir auf die Fehlermeldung von Python Acht geben, gibt sie uns auch die genaue Stelle an, wo der Fehler entdeckt wurde. Wir fangen also an, das Programm ab dieser Zeile zu debuggen.

Bei genauerer Untersuchung erkennen wir, dass die eine logische Zeile in zwei physikalische Zeilen aufgespalten worden ist, aber dass wir nicht angegeben haben, dass diese zwei physikalischen Zeilen zusammengehören. Für Python fehlt dem Summierungs-Operator (+) am Ende der logischen Zeile daher ein Operand, weswegen es an dieser Stelle abbricht. Erinnern Sie sich daran, dass wir angeben können, dass die logische Zeile in der folgenden physikalischen Zeile fortgeführt wird, indem wir einen umgekehrten Schrägstrich am Ende der physikalischen Zeile benutzen. Wir machen daher diese Verbesserung an unserem Programm. Eine solche Fehlerkorrektur wird auch als Bug-Fixing bezeichnet.

Die vierte Version

Beispiel 10.4. Sicherungsskript - vierte Version (sicherung_ver4.py)

				
#!/usr/bin/python

import os
import time

# 1. Die Dateien und Verzeichnisse, die gesichert werden sollen,
# werden in der folgenden Liste angegeben:
quellen = ['/home/swaroop/byte', '/home/swaroop/bin']
# Unter Windows muessen Sie die Pfade auf diese Weise angeben:
# quellen = ['C:\\Dokumente', 'D:\\Arbeit']

# 2. Die Sicherung muss in das folgende Hauptverzeichnis fuer
# Sicherungen gespeichert werden:
ziel_verzeichnis = '/mnt/e/sicherung/'
# Denken Sie daran, dies an Ihre Gegebenheiten anzupassen.

# 3. Die Dateien werden in einer ZIP-Datei gesichert.

# 4. Das Tagesdatum ist der Name des Unterverzeichnisses:
heute = ziel_verzeichnis + time.strftime('%Y%m%d')
# Die aktuelle Uhrzeit ist der Name des ZIP-Archivs:
jetzt = time.strftime('%H%M%S')

# Eine Anmerkung als Benutzereingabe entgegennehmen,
# die fuer den Namen der ZIP-Datei verwendet wird:
anmerkung = raw_input('Geben Sie eine Anmerkung ein --> ')
if len(anmerkung) == 0: # pruefe, ob eine Anmerkung eingegeben wurde
	ziel = heute + os.sep + jetzt + '.zip'
else:
	ziel = heute + os.sep + jetzt + '_' + \
		anmerkung.replace(' ', '_') + '.zip'
	# Beachten Sie den umgekehrten Schraegstrich!

# Erzeuge das Unterverzeichnis, wenn noch nicht vorhanden:
if not os.path.exists(heute):
	os.mkdir(heute) # erzeuge das Verzeichnis
	print 'Verzeichnis', heute, 'erfolgreich angelegt'

# 5. Wir benutzen den Befehl zip (unter Unix/Linux), um die Dateien
# zu einem ZIP-Archiv zu komprimieren:
zip_befehl = 'zip -qr %s %s' % (ziel, ' '.join(quellen))
# Windows-Benutzer koennen z.B. PKZIP oder Info-ZIP in das
# Windows-Systemverzeichnis kopieren, damit dies funktioniert.

# Sicherung starten
if os.system(zip_befehl) == 0:
	print 'Erfolgreiche Sicherung nach', ziel
else:
	print 'Sicherung fehlgeschlagen!'
				
				

Ausgabe

				
$ python sicherung_ver4.py
Geben Sie eine Anmerkung ein --> neue beispiele ergaenzt
Erfolgreiche Sicherung nach /mnt/e/backup/20041208/082156_neue_beispiele_ergaenzt.zip

$ python sicherung_ver4.py
Geben Sie eine Anmerkung ein -->
Erfolgreiche Sicherung nach /mnt/e/backup/20041208/082316.zip
				
				

So funktioniert es

So funktioniert das Programm jetzt! Wir wollen die eigentlichen Änderungen durchgehen, die wir in Version 3 gemacht haben. Wir nehmen eine Anmerkung des Benutzers mit der Funktion raw_input als Eingabe entgegen und prüfen, ob der Benutzer wirklich irgendetwas eingegeben hat, indem wir die Länge der Eingabe mit der Funktion len ermitteln. Wenn der Benutzer aus irgendeinem Grund (zum Beispiel, weil es nur eine Routine-Sicherung ist, und keine besonderen Änderungen vorgenommen wurden) nur einfach die Enter-Taste gedrückt hat, dann verfahren wir genauso wie vorher.

Wenn jedoch eine Anmerkung eingegeben wurde, dann wird diese an den Namen des ZIP-Archivs angehängt, direkt vor der Dateiendung .zip. Beachten Sie, dass wir Leerzeichen in der Anmerkung durch Unterstriche ersetzen - aus dem Grund, dass solche Dateinamen leichter handhabbar sind.

Weitere Verfeinerungen

Die vierte Version ist für die meisten Benutzer ein zufrieden stellend arbeitendes Skript, aber es gibt stets Möglichkeiten zur weiteren Verbesserung. Zum Beispiel könnte man einen Grad der Ausführlichkeit der Programmausgaben einbauen, den man mit einer Option -v angeben könnte, und der das Programm dazu bringt, geschwätziger in der Ausgabe zu sein.

Eine weitere mögliche Verbesserung würde es erlauben, dem Skript zusätzliche Dateien und Verzeichnisse auf der Kommandozeile zu übergeben. Wir könnten diese Übergabeparameter der Liste sys.argv entnehmen, und sie der Liste quellen hinzufügen, indem wir die extend-Methode benutzen, die von der Klasse list bereitgestellt wird.

Eine Verfeinerung, die ich bevorzuge, ist die Verwendung des tar-Befehls anstelle des zip-Befehls. Ein Vorteil hierbei ist, dass die Sicherung viel schneller durchgeführt wird, und die Sicherungsdatei außerdem viel kleiner wird, wenn man den tar-Befehl zusammen mit gzip verwendet. Wenn ich dieses Archiv unter Windows benötige, dann werden .tar.gz-Dateien auch problemlos von WinZip entpackt. Der tar-Befehl ist standardmäßig auf den meisten Linux/Unix-Systemen vorhanden. Windows-Benutzer können ihn sich ebenfalls aus dem Internet holen und auf ihrem Windows-System installieren.

Die entsprechende Befehlszeile sähe dann zum Beispiel so aus:

			
tar = 'tar -cvzf %s %s -X ausnahmen.txt' % (ziel, ' '.join(quellen))
			
			

Die Optionen werden im Folgenden erläutert.

  • -c (für create) zeigt an, dass eine Archivdatei erzeugt werden soll.

  • -v (für verbose) bedeutet, dass die Ausgabe des Programms geschwätziger sein sollte.

  • -z bedeutet, dass gzip als Filter benutzt werden soll.

  • -f (für force) bedeutet, dass beim Anlegen der Archivdatei eine eventuell bereits vorhandene Datei gleichen Namens überschrieben werden soll.

  • -X (für exclude) gibt eine Ausnahmedatei an, d.h. eine Textdatei, die eine Liste von Dateinamen enthält, die von der Sicherung ausgeschlossen werden sollen. Zum Beispiel könnte man in diese Datei den Eintrag *~ schreiben, wenn man nicht möchte, dass Dateien, deren Name mit dem Zeichen ~ endet (gewöhnlicherweise sind das temporär angelegte Dateien), mit in die Sicherung aufgenommen werden sollen.

Wichtig

Die bevorzugte Weise, solche Archive anzulegen, ist unter Verwendung des Moduls zipfile bzw. tarfile- Sie gehören zur Python-Standard-Bibliothek und stehen Ihnen bereits zum Gebrauch zur Verfügung. Durch den Einsatz dieser Bibliotheken kann man auch die Benutzung von os.system vermeiden, was grundsätzlich ratsam ist, weil man bei Benutzung dieser Funktion sehr schnell kostspielige Fehler machen kann.

Ich habe dennoch aus didaktischen Gründen os.system im Sicherungskript verwendet, damit das Beispiel einfach genug ist, dass es jeder verstehen kann, und real genug, um nützlich zu sein.