Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
Shell-Programmierung von Jürgen Wolf
Einführung, Praxis, Referenz
Buch: Shell-Programmierung

Shell-Programmierung
782 S., mit CD, 44,90 Euro
Rheinwerk Computing
ISBN 3-89842-683-1
gp Kapitel 15 Die Praxis
  gp 15.1 Alltägliche Lösungen
    gp 15.1.1 Auf alphabetische und numerische Zeichen prüfen
    gp 15.1.2 Auf Integer überprüfen
    gp 15.1.3 echo mit oder ohne -n
  gp 15.2 Datei-Utilities
    gp 15.2.1 Leerzeichen im Dateinamen ersetzen
    gp 15.2.2 Dateiendungen verändern
    gp 15.2.3 Nach veränderten Dateien in zwei Verzeichnissen vergleichen
  gp 15.3 Systemadministration
    gp 15.3.1 Benutzerverwaltung
    gp 15.3.2 Systemüberwachung
  gp 15.4 Backup-Strategien
    gp 15.4.1 Warum ein Backup?
    gp 15.4.2 Sicherungsmedien
    gp 15.4.3 Varianten der Sicherungen
    gp 15.4.4 Bestimmte Bereiche sichern
    gp 15.4.5 Backup über ssh mit tar
    gp 15.4.6 Daten mit rsync synchronisieren
    gp 15.4.7 Dateien und Verzeichnisse per E-Mail versenden
    gp 15.4.8 Startup-Scripts
  gp 15.5 World Wide Web und HTML
    gp 15.5.1 Analysieren von access_log (Apache)
    gp 15.5.2 Analysieren von error_log (Apache)
  gp 15.6 CGI (Common Gateway Interface)
    gp 15.6.1 CGI-Scripts ausführen
    gp 15.6.2 CGI-Environment ausgeben
    gp 15.6.3 Einfache Ausgabe als Text
    gp 15.6.4 Ausgabe als HTML formatieren
    gp 15.6.5 Systeminformationen ausgeben
    gp 15.6.6 Kontaktformular
    gp 15.6.7 Noch ein Tipp


Rheinwerk Computing

15.4 Backup-Strategiedowntop

Beim Thema Backup handelt es sich um ein sehr spezielles und vor allem enorm wichtiges Thema, weshalb hier eine umfassendere Einführung unumgänglich ist. Anhand dieser kurzen Einführung werden Sie schnell feststellen, wie viele Aspekte es gibt, die man bei der richtigen Backup-Lösung beachten muss. Zwar finden Sie anschließend auch einige Scripts in der Praxis dazu, doch handelt es sich bei diesem Thema schon eher um einen sehr speziellen Fall, bei dem man einiges Wissen benötigt und auch so manches berücksichtigen muss, sodass sich keine ultimative Lösung erstellen lässt.


Rheinwerk Computing

15.4.1 Warum ein Backup?  downtop

Es gibt drei verschiedene Fälle von Datenverlusten:

gp  Eine einzelne Datei (oder wenige Dateien) wird aus Versehen gelöscht oder falsch modifiziert (bspw. von einem Virus infiziert). In solch einem Fall ist das Wiederherstellen einer Datei häufig nicht allzu kompliziert. Bei solchen Daten ist eine tar-, cpio- oder afio-Sicherung recht schnell wieder zurückgeladen. Natürlich ist dies immer abhängig vom Speichermedium. Wenn Sie dieses Backup auf einem Band linear suchen müssen, ist das Wiederherstellen nicht unbedingt vorteilhaft. Hierzu ist es häufig sinnvoll, immer wieder ein Backup in einem anderen (entfernten) Verzeichnis zu abzuspeichern.
gp  Eine Festplatte ist defekt. Häufig denkt man, ist bei mir noch nie vorgekommen, aber wenn es dann mal crasht, ist guter Rat oft teuer. Hier gibt es eine recht elegante Lösung, wenn man RAID-Systeme im Level 1 oder 5 verwendet. Dabei werden ja die Daten redundant auf mehreren Platten gespeichert. Das bedeutet, dass Sie beim Ausfall einer Festplatte die Informationen jederzeit von der Kopie wieder holen können. Sobald Sie die defekte Platte gegen eine neue austauschen, synchronisiert das RAID-System die Platten wieder, sodass alle Daten nach einer gewissen Zeit wieder redundant auf den Platten vorhanden sind. Zur Verwendung von RAID gibt es eine Software- und eine Hardwarelösung mit einem RAID-Controller. Zwar ist die Hardwarelösung erheblich schneller, aber auch teurer.
    Auf der anderen Seite sollte man bei der Verwendung von RAID bedenken, dass jeder Fehler, der z. B. einem Benutzer oder gar dem Administrator selbst unterläuft, wie bspw. Software-Fehler, Viren, instabiles System, versehentliches Löschen von Daten usw., sofort auf das RAID-System bzw. auf alle Speichermedien im System repliziert wird.
       
gp  Datenverlust durch Hardware- oder Softwarefehler oder Elementarschäden (wie Feuer, Wasser oder Überspannung). Hier wird eine Komplettsicherung der Datenbestände nötig, was bei den Giga- bis Terrabytes an Daten, die häufig vorliegen, kein allzu leichtes Unterfangen darstellt. Gewöhnlich geht man hierbei zwei Wege:
Man schiebt die Daten auf einen entfernten Rechner (bspw. mit cp oder scp), am besten gleich in komprimierter Form (mittels gzip oder bzip2). Die wohl gängigste Methode dürfte das Archivieren auf wechselbaren Datenträgern wie Magnetband, CD oder DVD sein (abhängig vom Datenumfang). Meistens werden hierzu die klassischen Tools wie cp, dd, scp oder rsync verwendet. Auch sollte man unter Umständen eine Komprimierung mit gzip oder bzip2 vorziehen. Für Bänder und Streamer kommen häufig die klassischen Tools wie tar, afio, cpio oder taper zum Einsatz.
Rheinwerk Computing

15.4.2 Sicherungsmedien  downtop

Über das Speichermedium macht man sich wohl zunächst keine allzu großen Gedanken. Meistens greift man auf eine billigere Lösung zurück. Ohne auf die einzelnen Speichermedien genauer einzugehen, gibt es hierbei einige Punkte, die es zu überdenken gilt:

gp  Maximales Speichervolumen – will man mal eben schnell sein lokales Verzeichnis sichern, wird man wohl mit einer CD bzw. DVD als Speichermedium auskommen. Doch bevor Sie vorschnell urteilen, lesen Sie am besten noch die weiteren Punkte.
gp  Zugriffsgeschwindigkeit – wenn Sie wichtige Datenbestände im Umfang von mehreren Gigabytes schnell wiederherstellen müssen, werden Sie wohl kaum ein Speichermedium wählen, welches nur 1 MB in der Sekunde übertragen kann. Hier gilt es also, die Transferrate in MB pro Sekunde im Auge zu behalten.
gp  Zuverlässigkeit – wie lange ist die Haltbarkeit von Daten auf einer CD oder DVD oder gar auf einem Magnetband. Darüber werden Sie sich wohl bisher recht selten Gedanken gemacht haben – doch es gibt in der Tat eine durchschnittliche Haltbarkeit von Daten auf Speichermedien.
gp  Zulässigkeit – in manchen Branchen, z. B. in der Buchhaltung, ist es gesetzlich vorgeschrieben, nur einmal beschreibbare optische Datenträger zu verwenden. Bei der Verwendung von mehrfach beschreibbaren Medien sollte man immer die Risiken bedenken, dass diese wieder beschrieben werden können (sei es nun mit Absicht oder aus Versehen).

Rheinwerk Computing

15.4.3 Varianten der Sicherungen  downtop

Generell kann man von zwei Varianten einer Sicherung sprechen:

gp  Vollsicherung – dabei werden alle zu sichernden Daten vollständig gesichert. Der Vorteil ist, dass man jederzeit ohne größeren Aufwand die gesicherten Daten wieder zurückladen kann. Man sollte allerdings bedenken, ob man bestimmte Daten bei einer Vollsicherung ausgrenzen sollte – besonders die sicherheitsrelevanten. Der Nachteil daran ist ganz klar: Eine Vollsicherung kann eine ganz schöne Menge an Platz (und auch Zeit) auf einem Speichermedium verbrauchen.
gp  Inkrementelle Sicherung – hierbei wird nur einmal eine Vollsicherung vorgenommen. Anschließend werden immer nur diejenigen Daten gesichert, die sich seit der letzten Sicherung verändert haben. Hierbei wird weniger Platz auf einem Speichermedium (und auch Zeit) benötigt.

Rheinwerk Computing

15.4.4 Bestimmte Bereiche sichern  downtop

Hierzu einige kurze Vorschläge, wie man bestimmte Bereiche sinnvoll sichert.

gp  Einzelne Dateien – einzelne Dateien kann man mal schnell mit cp oder scp auf eine andere Festplatte, Diskette oder ein anderes Verzeichnis übertragen. Natürlich steht Ihnen hierzu auch die Möglichkeit mittels tar, afio oder cpio zur Verfügung, um die Daten auf externe Datenträger zu sichern. Gewöhnlich werden diese Archive auch noch mittels gzip oder bzip2 komprimiert. Gern werden hierzu auch Datenträger wie CD oder DVD verwendet.
gp  Dateibäume – ganze Dateibäume beinhalten häufig eine Menge Daten. Hier verwendet man als Speichermedium häufig Streamer, Magnetbänder oder optische Datenträger (CD, DVD), je nach Umfang. Als Werkzeuge werden gewöhnlich tar, afio und cpio eingesetzt. Aber auch Programme zur Datensynchronisation wie rsync oder unison werden oft genutzt. Zum Sichern auf optische Speichermedien wie CD oder DVD werden gewöhnlich die Tools mkisofs, (oder mit GUI) xcdroast, K3b oder gtoaster verwendet.
gp  Ganze Festplatte – zum Sichern ganzer Festplatten verwendet man unter Linux häufig das Programmpaket amanda (Advanced Maryland Automatic Network Disk Archiver), das ein komplettes Backup-System zur Verfügung stellt. Da es nach dem Client-Server-Prinzip arbeitet, ist somit auch eine Datensicherung über das Netzwerk möglich. Der Funktionsumfang von amanda ist gewaltig, weshalb ich hier auf die Webseite http://www.amanda.org.
gp  Dateisysteme – mithilfe des Kommandos dd lässt sich ein komplettes Dateisystem auf eine andere Platte oder auf ein Band sichern. Beachten Sie allerdings, dass beim Duplizieren beide Partitionen die gleiche Größe haben müssen (falls Sie Festplatten duplizieren wollen) und bestenfalls beide Platten nicht gemountet sind (zumindest nicht die Zielplatte). Da dd selbst physikalisch Block für Block kopiert, kann das Tool nicht auf defekte Blöcke überprüfen. Daher sollte man hierbei auch gleich noch mit dem Kommando badblocks nach defekten Blöcken suchen. Natürlich müssen Sie auch beim Zurückspielen darauf achten, dass die Zielpartition nicht kleiner als die Quellpartition ist. Ein anderes Tool, das auch auf Low-Level-Ebene und fehlertolerant arbeitet, ist dd_rescue.
    Ein weiteres hervorragendes Tool zum Sichern und Wiederherstellen ganzer Partitionen ist partimage, welches auch wie dd und dd_rescue in der Lage ist, unterschiedliche Dateisysteme zu sichern (ext2/ext3; reiserfs, xfs, UFS (Unix), HFS (MAC), NTFS/FAT16/FAT32 (Win32)), da es wie die beiden anderen genannten Tools auf Low-Level-Ebene arbeitet. Mehr zu partimage entnehmen Sie bitte http://www.partimage.org. Natürlich bietet sich Ihnen auch die Möglichkeit, Dateisysteme zu synchronisieren. Hierbei stehen Ihnen Werkzeuge wie rsync, unision, WebDAV usw. zur Verfügung.
       

Rheinwerk Computing

15.4.5 Backup über ssh mit tar  downtop

Das Sichern von Dateien zwischen Servern ist eigentlich mit dem Kommando scp recht einfach:

scp some-archive.tgz user@host:/home/backups

Nachteile von scp beim Durchlaufen ganzer Verzeichnisbäume (mit der Option –r) sind die vielen Kommandoaufrufe (alles wird einzeln kopiert) und die unflexiblen Kompressionsmöglichkeiten.

Hierzu wird in der Praxis oft tar verwendet. Verknüpfen wir nun tar mit ssh, haben wir exakt das, was wir wollen. Wenn Sie nämlich ssh ohne eine interaktive Login-Sitzung starten, erwartet ssh Daten von der Standardeingabe und gibt das Ergebnis auf die Standardausgabe aus. Das hört sich stark nach der Verwendung einer Pipe an. Wollen Sie beispielsweise alle Daten aus Ihrem Heimverzeichnis auf einem entfernten Rechner archivieren, können Sie wie folgt vorgehen:

tar zcvf – /home | ssh user@host "cat > homes.tgz"

Natürlich ist es möglich, das komprimierte Archiv auch auf ein Magnetband des entfernten Rechners zu schreiben (entsprechendes Medium und Rechte vorausgesetzt):

tar zcvf – /home | ssh user@host "cat > /dev/tape"

Wollen Sie stattdessen eine Kopie einer Verzeichnisstruktur auf Ihrer lokalen Maschine direkt auf das Filesystem einer anderen Maschine kopieren, so können Sie dies so erreichen (Sie synchronisieren das entfernte Verzeichnis mit dem lokalen):

cd /home/us10129/www.pronix.de ; tar zcf – html/ \
 | ssh user@host \
 "cd /home/us10129/www.pronix.de; mv html html.bak; tar zpxvf -"

Hier sichern Sie u. a. auch das Verzeichnis html auf host, indem Sie es umbenennen (html.bak) – für den Fall der Fälle. Dann erstellen Sie eine exakte Kopie von /home/us10129/www.pronix.de/html – Ihrem lokalen Verzeichnis – mit sämtlichen identischen Zugriffsrechten und der Verzeichnisstruktur auf dem entfernten Rechner. Da hierbei tar mit der Option z verwendet wird, werden die Daten vor dem »Hochladen« komprimiert, was natürlich bedeutet, dass eine geringere Datenmenge transferiert werden muss und der Vorgang erheblich schneller vonstatten gehen kann. Natürlich ist dies abhängig von der Geschwindigkeit beider Rechner, also wie schnell bei diesen die (De-)Kompression durchgeführt werden kann.

Müssen Sie auf dem entfernten Rechner etwas wiederherstellen und verfügen über ein Backup auf der lokalen Maschine, ist dies mit folgender Kommandoverkettung kein allzu großes Unterfangen mehr:

ssh user@host "cd /home/us10129/www.pronix.de; tar zpvxf -" \
   < big-archive.tgz

So stellen Sie das komplette Verzeichnis /home/us10129/www.pronix.de/ mit dem Archiv big-archive.tgz wieder her. Gleiches können Sie natürlich auch jederzeit in die andere Richtung vornehmen:

ssh user@host "cat big-archive.tgz" | tar zpvxf -

Damit Sie nicht andauernd ein Passwort eingeben müssen, empfiehlt es sich auch hier, SSH-Schlüssel zu verwenden (siehe Abschnitt 14.12.12). Das folgende Script demonstriert Ihnen die Möglichkeit, ssh und tar in einem Backup-Script zu verwenden.

#!/bin/sh
# Name: ssh_tar
# Backups mit tar über ssh
# Konfiguration, entsprechend anpassen
#
SSH_OPT="-l"
SSH_HOST="192.135.147.2"
SSH_USER="jwolf"
# Default-Angaben
#
LOCAL_DIR="/home/tot/backups"
REMOTE_DIR="/home/jwolf/backups"
stamp=`date +%d_%m_%Y`
BACKUP_FILE="backup_${stamp}.tgz"
usage() {
   echo "usage: star [-ph] [-pl] [-sh] [-sl] [-r] [-l] ..."
   echo
   echo "Optionen : "
   echo " –ph : (lokales) Verzeichnis packen und hochladen " \
        " (remote) in 'REMOTE_DIR'"
   echo "     Beispiel:   star -ph lokalesVerzeichnis "
   echo " -pl = (remote) Verzeichnis packen und runterladen"\
        " (lokal) in 'LOCAL_DIR'"
   echo "     Beispiel:   star -pl remoteVerzeichnis "
   echo " -sh = Synchronisiert ein Host-Verzeichnis mit einem "\
        "lokalen Verzeichnis"
   echo "     Beispiel:   star -sh lokalesVerzeichnis "\
        "remoteVerzeichnis syncVerzeichnis "
   echo " -sl = Synchronisiert ein lokales Verzeichnis mit "\
        "einem Host-Verzeichnis"
   echo "     Beispiel:   star -sl remoteVerzeichnis "\
        "lokalesVerzeichnis syncVerzeichnis "
   echo " -r  = (remote) Wiederherstellen eines"\
        " Host-Verzeichnisses"
   echo "     Beispiel:   star -r remoteVerzeichnis "\
        "lokalTarArchiv.tgz"
   echo " -l  = (lokal)  Wiederherstellen eines lokalen "\
        "Verzeichnisses"
   echo "     Beispiel:   star -l lokalesVerzeichnis "\
        "remoteTarArchiv.tgz"
   # ...
   exit 1
}
case "$1" in
    -ph)
       if [ $# -ne 2 ]
       then
          usage
       else
          cd $2; tar zcvf – "." | \
          ssh $SSH_OPT $SSH_USER $SSH_HOST \
          "cat > ${REMOTE_DIR}/${BACKUP_FILE}"
          echo "Verzeichnis '$2' nach "\
               "${SSH_HOST}:${REMOTE_DIR}/${BACKUP_FILE} "\
               "gesichert"
       fi ;;
    -pl)
       if [ $# -ne 2 ]
       then
          usage
       else
          ssh $SSH_OPT $SSH_USER $SSH_HOST \
          "cd $2; tar zcvf – ." | \
          cat > ${LOCAL_DIR}/${BACKUP_FILE}
          echo "Verzeichnis ${SSH_HOST}:${2} nach "\
               "${LOCAL_DIR}/${BACKUP_FILE} gesichert"
       fi ;;
    -sh)
       if [ $# -ne 4 ]
       then
          usage
       else
          cd $2
          tar zcf – $4/ |
          ssh $SSH_OPT $SSH_USER $SSH_HOST \
          "cd $3; mv $4 ${4}.bak; tar zpxvf -"
          echo "Verzeichnis ${2}/${4} mit"\
               " ${SSH_HOST}:${3}/${4} synchronisiert"
       fi ;;
    -sl)
       if [ $# -ne 4 ]
       then
          usage
       else
          cd $3; mv $4 ${4}.bak
     ssh $SSH_OPT $SSH_USER $SSH_HOST "cd ${2}; tar zcvf – ${4}"\
     | tar zpvxf -
          echo "Verzeichnis ${SSH_HOST}:${2}/${4} mit"\
               " ${3}/${4} synchronisiert"
       fi ;;
    -r)
       if [ $# -ne 3 ]
       then
          usage
       else
          ssh $SSH_OPT $SSH_USER $SSH_HOST \
          "cd ${2}; tar zpvxf -" < $3
          echo "${SSH_HOST}:$2 mit dem Archiv $3 "\
               "Wiederhergestellt"
       fi ;;
    -l)
       if [ $# -ne 3 ]
       then
          usage
       else
          cd $2
          ssh $SSH_OPT $SSH_USER $SSH_HOST "cat $3" | \
          tar zpvxf -
          echo "$2 mit dem Archiv ${SSH_HOST}:${3} "\
               "Wiederhergestellt"
       fi ;;
    -*)   usage;;
     *)   usage;;
esac

Das Script bei der Ausführung:

you@host > ./ssh_tar -ph Shellbuch_aktuell/
./
./kap004.txt
./kap005.txt
...
...
./Kap013.txt
./Kap014.txt
Verzeichnis 'Shellbuch_aktuell/' nach  
192.135.147.2:/home/jwolf/backups/backup_20_05_2005.tgz  gesichert

Ein Blick zum Rechner »192.135.147.2«:

jwolf@jwolf$ ls backups/
backup_20_05_2005.tgz
you@host > ./ssh_tar -pl backups/
./
./backup_20_05_2005.tgz
./kap004.txt
./kap005.txt
...
...
./Kap012.txt
./Kap013.txt
./Kap014.txt
Verzeichnis 192.135.147.2:backups/ nach  /home/you/backups/
backup_20_05_2005.tgz gesichert
you@host > ls backups/
backup_07_05_2005.tgz  backup_20_05_2005.tgz  Shellbuch

Erstellt im Remoteverzeichnis backups ein Ebenbild des Verzeichnisses Shellbuch_aktuell aus dem Heimverzeichnis des lokalen Rechners:

you@host > ./ssh_tar -sh $HOME backups Shellbuch_aktuell
Shellbuch_aktuell/
Shellbuch_aktuell/kap004.txt
Shellbuch_aktuell/kap005.txt
...
Shellbuch_aktuell/Kap013.txt
Shellbuch_aktuell/Kap014.txt
Verzeichnis /home/you/Shellbuch_aktuell mit 
192.135.147.2:backups/Shellbuch_aktuell synchronisiert

Erstellt im lokalen Heimverzeichnis $HOME/backup eine exakte Kopie des entfernten Remote-Verzeichnisses backups/Shellbuch:

you@host > ./ssh_tar -sl backups $HOME/backups Shellbuch_aktuell
Shellbuch_aktuell/
Shellbuch_aktuell/kap004.txt
Shellbuch_aktuell/kap005.txt
...
...
Shellbuch_aktuell/Kap013.txt
Shellbuch_aktuell/Kap014.txt
Verzeichnis 192.135.147.2:backups/Shellbuch_aktuell mit 
/home/tot/backups/Shellbuch_aktuell synchronisiert
you@host > ls backups/
backup_07_05_2005.tgz  backup_20_05_2005.tgz  Shellbuch_aktuell  
Shellbuch_aktuell.bak
you@host > ls backups/Shellbuch_aktuell
Kap003.txt  kap005.txt  Kap007.txt  Kap010.txt  Kap013.txt
...

Wiederherstellen eines entfernten Verzeichnisses mit einem lokalen Archiv. Im Beispiel wird das entfernte Verzeichnis backups/Shellbuch_aktuell mit dem lokalen Archiv backup_20_05_2005.tgz wiederhergestellt:

you@host > ls backups/
backup_07_05_2005.tgz  backup_20_05_2005.tgz  Shellbuch_aktuell  
Shellbuch_aktuell.bak
you@host > ./ssh_tar -r backups/Shellbuch_aktuell
> backups/backup_20_05_2005.tgz
./
./kap004.txt
./kap005.txt
...
...
./Kap013.txt
./Kap014.txt
192.135.147.2:backups/Shellbuch_aktuell mit dem Archiv 
backups/backup_20_05_2005.tgz  wiederhergestellt

Dasselbe Beispiel in anderer Richtung. Hier wird das lokale Verzeichnis backups/Shellbuch_aktuell mit dem Archiv backup_20_05_2005.tgz, welches sich auf dem entfernten Rechner im Verzeichnis backups befindet, wiederhergestellt:

you@host > ./ssh_tar -l backups/Shellbuch_aktuell
> backups/backup_20_05_2005.tgz
./
./kap004.txt
./kap005.txt
...
...
./Kap013.txt
./Kap014.txt
backups/Shellbuch_aktuell mit dem Archiv 
192.135.147.2:backups/backup_20_05_2005.tgz wiederhergestellt

Hinweis   Natürlich funktioniert dieses Script wie hier demonstriert nur mit einem Key-Login (siehe Abschnitt 14.12.12). Da Backup-Scripts aber ohnehin chronolgoisch laufen sollten, ist ein ssh-key immer sinnvoll, da man das Passwort nicht in einer Textdatei speichern muss.



Rheinwerk Computing

15.4.6 Daten mit rsync synchronisieren  downtop

Was sich mit ssh und tar realisieren lässt, gelingt natürlich auch mit rsync. Das folgende einfache Script synchronisiert entweder ein lokales Verzeichnis mit einem entfernten Verzeichnis oder umgekehrt. Um hierbei auch die Vertraulichkeit zu gewährleisten, »tunnelt« man das Ganze durch ssh. Das folgende Script demonstriert Ihnen, wie Sie rsync zum komfortablen Synchronisieren zweier entfernter Verzeichnisse verwenden können.

#!/bin/sh
# ssyncron
# Script zum Synchronisieren von Daten
usage() {
   echo "usage: prgname [-option] [Verzeichnis]"
   echo
   echo "-u : Ein Verzeichnis auf dem Server mit einem"\
        " lokalen synchronisieren"
   echo "-d : Ein lokales Verzeichnis mit einem Verzeichnis"\
        " auf dem Server synchronisieren"
   exit 1
}
# Konfigurationsdaten
#
# Pfad zu den Daten (Lokal)
local_path="$HOME/"
# Pfad zu den Dateien (Server)
remote_path="/home/us10129"
# Loginname
username="us10129@myhoster.de"
# Optionen zum Download '-d'
D_OPTIONS="-e ssh -av --exclude '*.xvpics' --exclude 'cache' --exclude 'bestellen'"
# Optionen zum Hochladen '-u'
U_OPTIONS="-e ssh -av"
# rsync vorhanden ...
if [ `which rsync` = "" ]
then
   echo "Das Script benötigt 'rsync' zur Ausführung ...!"
   exit 1
fi
# Pfad zu rsync
RSYNC=`which rsync`
site=$2
case "$1" in
   # Webseite herunterladen – Synchronisieren Lokal mit Server
   -d)
      [ -z $2 ] && usage # Verzeichnis fehlt ...
      $RSYNC $D_OPTIONS \
      $username:${remote_path}/${site}/ ${local_path}${site}/ ;;
   # Webseite updaten – Synchronisieren Server mit Lokal
   -u)
      $RSYNC $U_OPTIONS \
      ${local_path}${site}/ $username:${remote_path}/${site}/ ;;
   -*)
      usage ;;
    *)
      usage ;;
esac

Das Script bei der Ausführung:

Entferntes Verzeichnis mit dem lokalen Verzeichnis synchronisieren:

you@host > ./ssyncron -d backups/Shellbuch
receiving file list ... done
./
Martin/
Kap001.doc
Kap001.sxw
Kap002.sxw
Kap003.txt
Kap007.txt
...
...
Martin/Kap001.doc
Martin/Kap002.sxw
Martin/Kap003.sxw
Martin/Kap004.sxw
Martin/Kap005.sxw
kap004.txt
kap005.txt
newfile.txt
whoami.txt
wrote 516 bytes  read 1522877 bytes  38566.91 bytes/sec
total size is 1521182  speedup is 1.00

Eine neue lokale Datei atestfile.txt erzeugen und in das entfernte Verzeichnis synchronisieren:

you@host > touch backups/Shellbuch/atestfile.txt
you@host > ./ssyncron -u backups/Shellbuch
building file list ... done
Shellbuch/
Shellbuch/atestfile.txt
wrote 607 bytes  read 40 bytes  86.27 bytes/sec
total size is 1521182  speedup is 2351.13

Einige Dateien im lokalen Verzeichnis Shellbuch löschen (Datenverlust simulieren) und anschließend mit dem entfernten Verzeichnis Shellbuch wiederherstellen (synchronisieren):

you@host > rm backups/Shellbuch/Kap00[1–9]*
you@host > ./ssyncron -d backups/Shellbuch
receiving file list ... done
./
Kap001.doc
Kap001.sxw
Kap002.sxw
Kap003.txt
Kap007.txt
Kap008.txt
Kap009.txt
wrote 196 bytes  read 501179 bytes  28650.00 bytes/sec
total size is 3042364  speedup is 6.07

Wenn in beiden Richtungen nichts mehr zu tun ist, dann ist alles synchronisiert:

you@host > ./ssyncron -u backups/Shellbuch
building file list ... done
wrote 551 bytes  read 20 bytes  87.85 bytes/sec
total size is 1521182  speedup is 2664.07
you@host > ./ssyncron -d backups/Shellbuch
receiving file list ... done
wrote 56 bytes  read 570 bytes  96.31 bytes/sec
total size is 1521182  speedup is 2430.00

Hinweis   In der Praxis würde es sich außerdem anbieten, rsync mit den Optionen -b für Backup zu verwenden, womit Backup-Kopien alter Dateiversionen angelegt werden, sodass man gegebenenfalls auf mehrere Versionen zurückgreifen kann, und die Option -z zu nutzen, mit der Kompression möglich ist.

Hinweis   Hier gilt dasselbe wie schon beim Script zuvor. Hier sollten Sie ebenfalls einen ssh-key für ein Key-Login verwenden (siehe Abschnitt 14.12.12).



Rheinwerk Computing

15.4.7 Dateien und Verzeichnisse per E-Mail versenden  downtop

Sie wollen sich ein Backup mit cpio erstellen und es sich automatisch zukommen lassen. Als einfachster Weg würde sich hier der Transfer per E-Mail als Anhang eignen. Dank megabyteschwerer Postfächer sollte dies heutzutage kein Problem mehr sein. Allerdings lässt sich ein Anhang nicht einfach so hinzufügen. Hierzu benötigen Sie uuencode (siehe Abschnitt 14.12.6). Zuerst müssen Sie also cpio-typisch ein komplettes Verzeichnis durchlaufen und die Ausgabe gefundener Dateien (mit find) durch eine Pipe an cpio übergeben. Damit daraus eine einzige Datei als Anhang wird, schickt cpio wiederum die Ausgabe durch die Pipe an gzip, um das vollständige Verzeichnis zu komprimieren. Diesen »gzipten« Anhang müssen Sie nun mit uuencode enkodieren, um ihn daraufhin mit mail oder mailx (oder ggf. mit sendmail) an den gewünschten Absender zu schicken. Alle diese Aktionen werden durch eine Pipe an das andere Kommando geschickt:

   find "$Dir" -type f -print |
   cpio -o${option} |
   gzip |
   uuencode "$Archive" |
   $Mail -s "$Archive" "$User" || exit 1

Hierzu das komplette Script:

#!/bin/sh
# Name: mailcpio
# Archiviert Dateien und Verzeichnisse per cpio, komprimiert mit
# gzip und verschickt das Archiv an eine bestimmte E-Mail-Adresse
PROGN="$0"
# Benötigte Programme: mail oder mailx...
if [ "`which mailx`" != "" ]
then
   Mail="mailx"
   if [ "`which mail`" != "" ]
   then
      Mail="mail"
   fi
else
  echo "Das Script benötigt 'mail' bzw. 'mailx' zur Ausführung!"
  exit 1
fi
# Benötigt 'uuencode' für den Anhang
if [ "`which uuencode`" = "" ]
then
   echo "Das Script benötigt 'uuencode' zur Ausführung!"
   exit 1
fi
# Benötigt 'cpio'
if [ "`which cpio`" = "" ]
then
   echo "Das Script benötigt 'cpio' zur Ausführung!"
   exit 1
fi
Usage () {
   echo "$PROGN – Versendet ganze Verzeichnisse per E-Mail"
   echo "usage: $PROGN [option] e-mail-adresse"\
        " {datei|Verzeichnis} [datei|Verzeichnis] ..."
   echo
   echo "Hierbei werden alle angegebenen Dateien und "\
        "Verzeichnisse (inskl. Unterverzeichnisse)"
   echo "an eine angegebene Mail-Adresse gesendet. Das"\
        " Archiv wird mittels gzip komprimiert."
   echo "Option:"
   echo "-s : Keine Ausgabe von cpio"
   echo "-v : Macht cpio gesprächig"
   exit 1
}
while [ $# -gt 0 ]
do
   case "$1" in
      -v)   option=Bv ;;
      -s)   Silent=yes ;;
      --)   shift; break ;;
      -*)   Usage ;;
       *)   break ;;
    esac
    shift
done
if [ $# -lt 2 ]
then
   Usage
fi
User="$1"; shift
for Dir
do
   Archive="${Dir}.cpio.gz"
   # Verzeichnis nicht lesbar ...
   if [ ! -r "$Dir" ]
   then
      echo "Kann $Dir nicht lesen – (wird ignoriert)"
      continue
   fi
   [ "$Silent" = "" ] && echo "$Archive   ->    "
   find "$Dir" -type f -print |
   cpio -o${option} |
   gzip |
   uuencode "$Archive" |
   $Mail -s "$Archive" "$User" || exit 1
done

Das Script bei der Ausführung:

you@host > ./mailcpio-s pronix@t-online.de logfiles
3 blocks
you@host > ./mailcpio -v pronix@t-online.de logfiles
logfiles.cpio.gz   ->
logfiles/testscript.log.mail
logfiles/testscript.log
1 block

Rheinwerk Computing

15.4.8 Startup-Scripts  toptop

Wenn der Kernel gestartet wurde, kann dieser vorerst nur im Lese-Modus auf die Root-Partition zugreifen. Als ersten Prozess startet der Kernel init (/sbin/init) mit der PID 1. Dieser Prozess gilt ja als Elternteil aller weiteren Prozesse, die noch gestartet werden. Der Prozess init kümmert sich zunächst um die Konfiguration des Systems und den Start von zahlreichen Daemon-Prozessen.


Hinweis   Es muss gleich darauf hingewiesen werden, dass sich über init keine hundertprozentigen Aussagen treffen lassen. Hier kochen die jeweiligen Distributionen häufig Ihr eigenes Süppchen. Die Unterschiede beziehen sich wieder insbesondere auf diejenigen Verzeichnisse, in denen sich die Init-Dateien befinden, und auf die hierbei berücksichtigten Konfigurationsdateien. Natürlich heißt dies auch, dass die Init-Pakete verschiedener Distributionen gewöhnlich gänzlich inkompatibel sind und nicht untereinander ausgetauscht werden können. Daher folgt hier zunächst ein allgemeiner Überblick über den Init-Prozess (genauer System-V-init).


Bevor hier ein wenig ins Detail gegangen wird, zunächst ein kurzer Init-Überblick über einen normalen Systemstart:

gp  Nach dem Systemstart, wenn der Kernel geladen wurde, startet dieser das Programm (besser den Prozess) /sbin/init mit der PID 1.
gp  init wertet zunächst die Konfigurationsdatei /etc/inittab aus.
gp  Jetzt führt init ein Script zur Systeminitialisierung aus. Name und Pfad des Scripts sind stark distributionsabhängig.
gp  Als Nächstes führt init das Script rc aus, welches sich auch an unterscheidlichen Orten (und eventuell unter verschiedenen Namen) auf den Distributionen befindet.
gp  rc startet nun einzelne Scripts, die sich in einem Verzeichnis namens rc[n].d befinden. n ist hierbei der Runlevel. Diese Scriptdateien in den Verzeichnissen rc[n].d starten nun die Systemdienste (Daemonen), auch Startup-Scripts genannt. Der Speicherort dieser Verzeichnisse ist distributionsabhängig. Für gewöhnlich finden Sie die Verzeichnisse unter /etc oder unter /etc/init.d.

init und der Runlevel

Normalerweise verwendet init sieben Runlevel, die jeweils eine Gruppe von Diensten enthalten, die das System beim Starten (oder Beenden) ausführen soll. Sicherlich haben Sie schon davon gehört, dass Linux/UNIX ein Multi-User-Betriebssystem ist, also im Multi-User-Modus läuft. Es ist aber auch möglich, dass Sie Linux/UNIX im Single-User-Modus starten. Dass dies überhaupt realisiert werden kann, ist den verschiedenen Runlevel zu verdanken.

In der Datei /etc/inittab wird unter Linux der »default runlevel« festgelegt, es ist stark distributionsabhängig. Welcher Runlevel welche Dienste startet, ist von »Symlinks« in den einzelnen Verzeichnissen rc[n].d abhängig. Diese »Symlinks« zeigen meist auf die Startscripts der Dienste, die im Allgemeinen unter /etc/init.d abgelegt sind.

Zunächst ein kurzer Überblick zu den unterschiedlichen Runleveln und ihren Bedeutungen bei den gängigsten Distributionen:


Tabelle 15.1   Runlevel und ihre Bedeutung

Runlevel Bedeutung
0 Dieser Runlevel hält das System komplett an (Shutdown mit Halt).
1 oder S Single-User-Modus (Einzelbenutzer-Modus)
2 oder M Multi-User-Modus (Mehrbenutzer-Modus) ohne Netzwerk
3 Multi-User-Modus (Mehrbenutzer-Modus) mit einem Netzwerk, aber ohne X–Start
4 Dieser Runlevel wird gewöhnlich nicht verwendet und steht somit zur freien Verfügung.
5 Multi-User-Modus (Mehrbenutzer-Modus) mit einem Netzwerk und einem grafischen Login (X-Start). Nach dem Login wird gewöhnlich die grafische Oberfläche gestartet.
6 Dieser Runlevel startet das System neu (Shutdown mit Reboot).

Wie Sie sehen konnten, handelt es sich bei den Runleveln 0 und 6 um spezielle Fälle, in denen sich das System nicht lange halten kann. Hierbei wird das System heruntergefahren (Runlevel 0) oder eben neu gestartet (Runlevel 1). Zumeist werden die Runlevel 2 und 3 und die Mehrbenutzer-Level l und 5 verwendet, womit eine X-Anmeldeprozedur (wie xdm, kdm, gdm etc.) gestartet wird. Runlevel 1 (bzw. S) ist für die meisten Systeme unterschiedlich definiert und Runlevel 4 wird fast nie benutzt.


Hinweis   Linux unterstützt übrigens 10 Runlevel, wobei die Runlevel 7 bis 9 nicht definiert sind.


Runlevel 1 bzw. S

Im Runlevel 1, dem Einzelbenutzer-Modus, werden meist alle Mehrbenutzer- und Anmeldeprozesse beendet. Somit ist sicher, dass das System mit geringem Aufwand ausgeführt wird. Natürlich bietet Runlevel 1 vollen root-Zugriff auf das System. Damit ein System in Runlevel 1 auch nach einem root-Passwort abfragt, wurde der Runlevel S entwickelt. Unter System-V-init existiert kein echter Runlevel S und er dient daher nur dazu, das root-Passwort abzufragen. Dieser Runlevel ist gewöhnlich dazu gedacht, dass Administratoren diverse Patches einspielen können.

/etc/inittab

In der Datei /etc/inittab steht, was init auf den verschiedenen Runleveln zu tun hat. Wenn der Rechner gestartet wird, durchläuft init Runlevel 0 bis hin zum Runlevel, der in /etc/inittab als Standard (default runlevel) festgelegt wurde. Damit der Übergang von einem Level zum nächsten reibungslos läuft, führt init die in /etc/inittab angegebenen Aktionen aus. Dasselbe geschieht natürlich auch im umgekehrten Fall beim Herunterfahren bzw. Neustarten des Systems.

Die Verwendung von inittab ist allerdings nicht unbedingt das Gelbe vom Ei, weshalb noch zusätzliche Schichten in Form eines Scripts für das Wechseln der Runlevel eingebaut wurden. Dieses Script (rc – zu finden meist unter /etc/init.d/rc oder /etc/rc.d/rc) wird gewöhnlich aus inittab aufgerufen. Es (rc) führt wiederum weitere Scripts in einem vom Runlevel abhängigen Verzeichnis aus, um das System in seinen neuen (Runlevel-)Zustand zu versetzen.

In vielen Büchern wird jetzt hier die Datei /etc/inittab etwas genauer zerlegt und beschrieben (bspw. inittab-Schlüsselwörter), was natürlich sehr lehrreich ist, doch in der Praxis müssen Sie sich als Systemadministrator eigentlich nicht damit befassen, da die eben erwähnte Schnittstelle für jede Anwendung geeignet ist.

Startup-Scripts erstellen und ausführen

Die Terminologie von Startup-Scripts ist häufig nicht einfach zu durchschauen, weshalb hier ein Beispiel gegeben werden soll. Gewöhnlich finden Sie die Hauptkopien der Startup-Scripts im Verzeichnis /etc/inid.d.

you@host > ls -l /etc/init.d/
insgesamt 308
-rwxr-xr-x  1 root root  2570 2005–03–02 18:45 acpid
-rwxr-xr-x  1 root root  1098 2005–02–24 10:29 acpi-support
-rwxr-xr-x  1 root root 11038 2005–03–25 23:08 alsa
-rwxr-xr-x  1 root root  1015 2004–11–26 12:36 anacron
-rwxr-xr-x  1 root root  1388 2005–03–01 04:11 apmd
-rwxr-xr-x  1 root root  1080 2005–02–18 11:37 atd
-rw-r--r--  1 root root  2805 2005–01–07 19:35 bootclean.sh
-rwxr-xr-x  1 root root  1468 2005–01–07 19:36 bootlogd
-rwxr-xr-x  1 root root  1371 2005–01–07 19:35 bootmisc.sh
-rwxr-xr-x  1 root root  1316 2005–01–07 19:35 checkfs.sh
-rwxr-xr-x  1 root root  7718 2005–01–07 19:35 checkroot.sh
-rwxr-xr-x  1 root root  5449 2004–12–26 14:12 console-screen.sh
-rwxr-xr-x  1 root root  1168 2004–10–29 18:05 cron
...

Jedes dieser Scripts ist für einen Daemon oder einen anderen Aspekt des Systems verantwortlich. Und jedes dieser Scripts verarbeitet die Argumente »start« und »stop«, womit Sie den entsprechenden Dienst initialisieren oder beenden können, zum Beispiel:

# /etc/init.d/cron
 * Usage: /etc/init.d/cron start|stop|restart|reload|force-reload

Hier haben Sie versucht, das Script »cron«, welches für die Ausführung und Beendung des cron-Daemons verantwortlich, ist aufzurufen. Sie bekommen hierbei die möglichen Argumente mitgegeben, wie Sie das Script aufrufen können. Neben »start« und »stop« finden Sie häufig auch noch »restart«, was im Prinzip dasselbe bewirkt, wie ein »stop« mit anschließendem »start«. Des Weiteren findet man gewöhnlich auch eine Option »reload«, die den Dienst nicht beendet, sondern ihn auffordert, seine Konfiguration neu einzulesen (meist per Signal SIGHUP).

Im folgenden Beispiel soll das Script »sleep_daemon« beim Systemstart automatisch gestartet und beim Beenden wieder automatisch beendet werden. Hier das Script:

#!/bin/sh
# Name : sleep_daemon
sleep 1000 &

Das Script macht nichts anderes, als einen »schlafenden Dämon« zu erzeugen. Neben einfachen Shellscripts können Sie in der Praxis natürlich jedes andere Programm – sei es nun ein Binary oder ein Script – in beliebiger Sprache ausführen lassen. Tatsächlich können Sie alle Shellscripts in diesem Buch dazu verwenden (ob sinnvoll oder nicht). Dieses Script »sleep_daemon« habe ich nun in das Verzeichnis /usr/sbin verschoben und natürlich entsprechende Ausführrechte (für alle) gesetzt. Für diesen Vorgang werden Sie wohl root-Rechte benötigen. Alternativ können Sie auch das Verzeichnis /usr/local/sbin verwenden.

Folgendermaßen sieht nun das Startup-Script aus, womit Sie »sleep_daemon« starten, beenden und neu starten können.

#!/bin/sh
# Name : sleep_daemon
DAEMON="/usr/sbin/sleep_daemon"
test -f $DAEMON || exit 0
case "$1" in
   start)
        echo -n "Starte sleep_daemon"
        $DAEMON
        echo "."
        ;;
    stop)
        echo -n "Stoppe sleep_daemon"
        killall sleep
        echo "."
        ;;
    restart)
        echo -n "Stoppe sleep_daemon"
        killall sleep
        echo "."
        echo -n "Starte sleep_daemon"
        $DAEMON
        echo "."
        ;;
        # Hierzu wird auch gern folgende Sntax eingesetzt:
        # $0 stop
        # $0 start;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac

Ich habe hier denselben Namen verwendet, was Sie in der Praxis nicht tun müssen. Dieses Startup-Script können Sie nun in das Verzeichnis /etc/init.d verschieben und müssen es auch wieder ausführbar machen. Theoretisch können Sie jetzt schon den Dienst von Hand starten bzw. beenden:

# /etc/init.d/sleep_daemon start
Starte sleep_daemon.
# /etc/init.d/sleep_daemon restart
Stoppe sleep_daemon.
Starte sleep_daemon.
# /etc/init.d/sleep_daemon stop
Stoppe sleep_daemon.

Damit unser Dienst auch automatisch beim Eintreten bzw. Austreten eines bestimmten Runlevels gestartet bzw. beendet wird, benötigt das von init gestartete Master-Controll-Script (rc) zusätzliche Informationen, welche Scripts ausgeführt werden sollen. Denn anstatt im Verzeichnis /etc/init.d nachzublättern, wann bei welchem Runlevel ein Script gestartet werden muss, sieht das Master-Controll-Script in einem Verzeichnis namens rc[n].d (bspw. rc1.d; rc2.d; ... rc6.d) nach. n steht für den Runlevel, in den es gelangen will. Zum Beispiel hier unter »Ubuntu Linux« im Verzeichnis rc2.d:

# ls -l /etc/rc2.d/
lrwxrwxrwx  1 root root 17 K11anacron -> ../init.d/anacron
lrwxrwxrwx  1 root root 17 S05vbesave -> ../init.d/vbesave
lrwxrwxrwx  1 root root 18 S10sysklogd -> ../init.d/sysklogd
lrwxrwxrwx  1 root root 15 S11klogd -> ../init.d/klogd
lrwxrwxrwx  1 root root 14 S12alsa -> ../init.d/alsa
lrwxrwxrwx  1 root root 13 S14ppp -> ../init.d/ppp
lrwxrwxrwx  1 root root 16 S19cupsys -> ../init.d/cupsys
lrwxrwxrwx  1 root root 15 S20acpid -> ../init.d/acpid
lrwxrwxrwx  1 root root 14 S20apmd -> ../init.d/apmd
...

Sie können gleich erkennen, dass diese Einträge in rc[n].d gewöhnlich symbolische Links sind, die auf die Startup-Scripts im init.d-Verzeichnis verweisen. Wenn Sie die anderen Runlevel-Verzeichnisse ebenfalls ansehen, werden Sie feststellen, dass hierbei alle Namen der symbolischen Links entweder mit einem »S« oder einen »K«, gefolgt von einer Nummer und dem eigentlichen Dienst beginnen (bspw. S14alsa). Auch dies ist recht schnell erklärt. Steigt init von einem Runlevel in den nächst höheren Level auf, werden alle Scripts mit »S« beginnend ausgeführt. Diese Scripts werden also mit dem Argument »start« ausgeführt. Wenn init hingegen von einem höheren Runlevel in einen niedrigeren wechselt, werden alle Scripts mit »K« (kill) beginnend ausgeführt. Hierbei wird gewöhnlich das Script mit dem Argument »stop« ausgeführt. Die Nummern haben eine Art Prioritätsbedeutung. Die Scripts im Runlevel-Verzeichnis werden bei der Option »start« in alphabetischer und bei »stop« in umgekehrter Reihenfolge ausgeführt. Somit bedient man sich einer Nummerierung, um die Reihenfolge der Ausführung zu beeinflussen.

Jetzt wollen Sie natürlich auch Ihr Startup-Script hinzufügen. Um also dem System mitzuteilen, dass Sie einen Daemon starten wollen, müssen Sie ebenfalls einen symbolischen Link in das entsprechende Verzeichnis setzen. Die meisten Dienste starten Ihre Dämonen im Runlevel 2, weshalb hier ebenfalls ein Eintrag vorgenommen wird. Um den Daemon auch ordentlich wieder zu beenden, müssen Sie natürlich auch einen Link im Runlevel 0 eintragen. Da es bei einigen Systemen Unterschiede zwischen Shutdown und Reboot gibt, sollten Sie auch einen Eintrag im Runlevel 6 erstellen, um auf Nummer sicher zu gehen, dass sich der Daemon beim Neustart korrekt beendet hat.

# ln -s /etc/init.d/sleep_daemon /etc/rc2.d/S23sleep_daemon
# ln -s /etc/init.d/sleep_daemon /etc/rc0.d/K23sleep_daemon
# ln -s /etc/init.d/sleep_daemon /etc/rc6.d/K23sleep_daemon

Mit der ersten Zeile weisen Sie das System nun an, das Startup-Script /etc/init.d/sleep_daemon mit dem Argument »start« auszuführen, wenn es im Runlevel 2 angelangt ist. Mit der zweiten Zeile legen Sie fest, dass /etc/init.d/sleep_daemon beim Herunterfahren des Systems mit dem Argument »stop« beendet wird, wenn es im Runlevel 0 angekommen. ist. Gleiches nehmen Sie auch mit der dritten Zeile vor, nur eben für den Neustart des Systems in Runlevel 6.

Wenn Sie jetzt das System beenden und wieder hochfahren, können Sie in der Startup-Sitzung (sofern diese sichtbar ist) Ihren Dienst beim Starten beobachten. Hier werden Sie irgendwann ein Zeile wie

Starte sleep_daemon.

finden. Beim Beenden bzw. Neustarten des Systems entsteht diese Meldung, nur dass eben der »sleep_daemon« beendet wurde.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 << zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Shell-Programmierung
Shell-Programmierung
bestellen
 Buchtipps
Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-Programmierung


Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern