Logo


Computer Communication & Consulting

Linux - BASH Scripts

letzte Änderung 11.12.2007

sticksync - Shell Script zum Synchronisieren von USB-Disks


Funktionen von sticksync
Die Steuerdatei für sticksync - 0_sync
sticksync - Das Skript, Version 1.7

Linux Skripte ... kleine Helfer

sticksync Übersicht

Skript zur einfachen Synchronisierung von USB-Datenträgern

sticksync ist eine Weiterentwicklung meines Skripts syncstick. Wenn man viele Verzeichnisse bzw. Dateien mit syncstick aktuell halten möchte oder wenn mehrere USB-Disks bzw. USB-Sticks vorhanden sind, wird das kleine Skript sehr schnell groß und unübersichtlich.
sticksync liegt ein anderer Ansatz zugrunde. Es wird in jedem zu synchronisierenden Verzeichnis eine Datei 0_sync angelegt, in die die entsprechenden Aktionen eingetragen werden. syncstick sucht ab dem Home-Verzeichnis rekursiv durch den Verzeichnisbaum nach 0_sync Files und führt dann entsprechend dem gemounteten USB-Datenträger die definierten Aktionen durch. sticksync kann bedingt auch für Backup benutzt werden.


Funktionen von sticksync

sticksync ist ein Skript, dass die Verwendung von rsync automatisiert. sticksync sucht ab dem Home-Verzeichnis des Users durch den kompletten Verzeichnisbaum nach Steuerdateien "0_sync". Wird eine Datei namens "0_sync" gefunden, so wird sie im entsprechenden Verzeichnis verarbeitet. Das bedeutet, es wird nachgesehen, ob der entsprechende USB-Datenträger gemounted ist und dann wird rsync mit den entsprechenden Parametern aufgerufen.

Auf den ersten Blick erscheint die Arbeit 0_snyc Dateien in den unterschiedlichen Verzeichnissen anzulegen und zu pflegen etwas kompliziert. Da man aber die 0_sync Datei typischerweise pflegt, während man in diesem Verzeichnis arbeitet, z.B. im Sources Verzeichnis während der Programmierung, hält sich der Aufwand in Grenzen und die Übersichtlichkiet bleibt groß. Ich arbeite regelmäßig auf drei verschiedenen Linux-Rechnern und mir gelingt mit sticksync deren Synchronisierung problemlos.

sticksync kann reine Backups anlegen und sowohl unidirektional als auch bidirektional Verzeichnisse oder einzelne Dateien synchronisieren. Dazu gibt es die drei Anweisungen: back, copy und sync in der Steuerdatei. Zur Identifizierung der Sticks muss auf dem Stick im obersten (root) Verzeichnis eine Datei mit dem Namen des Sticks liegen. Es können mehrere Datenträger gleichzeitig gemountet sein und synchronisiert werden. Ein weiterer Vorteil ist das automatische Erkennen ob es sich um ein Linux oder VFAT Dateisystem handelt und die entsprechende Verwendung der "richtigen" rsync-Parameter.

Falls Sie Links in Ihrem Dateisystem verwenden, sollten Sie Ihren USB-Stick mit ext2 oder ext3 formatieren, damit die Links auch entsprechend sychronisiert werden können. Anderenfalls werden von rsync "Ungültige Dateien" reklamiert.

Optionen von sticksync (neu ab V 1.7)

Um das Testen und Synchronisieren einfacher zu gestalten, wurden in der Version 1.7 Kommandozeilen Parameter eingeführt. Diese sind optional und dienen hauptsächlich Testzwecken. Auch ist es manchmal wünschenswert nur einen bestimmten Ast des Dateisystems abzuarbeiten.

Der Syntax lautet: sticksync [opt] [path]
Dies ermöglicht das Prozessing eines einzelnen 0_sync Files, das mit path angegeben wird. Ist kein Pfad angegeben, so werden ab dem Home-Verzeichnis des Benutzers alle 0_sync gesucht und abgearbeitet. Wird als Path kein 0_sync File sondern ein Verzeichnis angegeben, so werden alle Unterverzeichnisse ab path durchsucht.

Mit den Optionen -d und -v können Debug-Mode und Verbose-Mode für das gesamte Prozessing eingeschaltet werden. Dies "überschreibt" die Einstellung in den einzelnen 0_sync Files.


Die Steuerdatei - 0_sync

Der Name wurde so gewählt, weil sie dann in den meisten Verzeichnissen gleich am Anfang zu finden ist. Der Name ist im Skript einstellbar.
Am einfachsten läßt sich der Aufbau der Steuerdatei anhand von Beispielen erläutern.

 1: ### sync in ~/bin ###
 2: #debug:
 3: verbose:
 4: # all pathes must have trailing /
 5:
 6: ### Beginn des Stick ###
 7: stick:STICK2G
 8:    copy-r:* $STICK/backup/bin/ !--exclude *.bak
 9:
10: ### Next Stick ###
11: stick:stick_emc
12:    sync:sticksync $STICK/bin/
13:    sync:qi $STICK/bin/
14:
15: ### Next Stick ###
16: stick:USB-HDD
17:     backupdir:$STICK/backup/amilo/
18:     back-r:. !--exclude *~
Beispiel 1 - kopieren und synchronisieren mit verschiedenen Sticks

Zeilen, die mit # beginnen, sind Kommentarzeilen. In den Zeilen 7, 11 und 16 beginnt jeweils der Abschnitt eines neuen Datenträgers. Diese Zeilen werden mit "stick:" eingeleitet. Alle Zeilen bis zum nächsten "stick:" betreffen diesen Stick. In Zeile 8 leitet "copy-r:" ein rekursives unidirektionales Kopieren von allen (=*) Dateien auf den USB-Stick ($STICK) in dessen Verzeichnis ./backup/bin/ ein, wobei die Dateien *.bak excludiert werden. Wird als Dateiname "*" angegeben um alle Dateien des Verzeichnisses zu kopieren, werden alle Dateie des Directories kopiert mit Ausnahme von 0_snyc.

Der Syntax der einzelnen Zeilen ist also:
Aktion:Source Destination [!Option(s)]
wobei es wichtig ist, Pfade (wenn es sich um Directories handelt) mit einem Slash "/" abzuschließen. Das Ausrufezeichen trennt die rsync Optionen vom Pfad ab und ist nur einmal zu verwenden. Zwischen Quelle und Ziel muss ein Leerzeichen stehen. Zwischen Ziel und dem "!" für Optionen kann das Leerzeichen entfallen.

In Zeile 12 wird die einzelne Datei "sticksync" bidirektional mit einem anderen STICK synchronisiert. Ebenso wie in Zeile 12 die Datei "qi".

In Zeile 16 wird der default Backup Pfad auf eine externe USB-HDD umgestellt. Die Backups werden dort in das Verzeichnis "amilo" abgelegt. Diese Umlenkung gilt nur für diesen Stick in diesem Verzeichnis. Mit back-r in Zeile 18 wird ein kompletter Backup inkl. Hidden-Files (Punkt Dateien) durchgeführt.

Die Angabe des Punktes als Dateinamen funktioniert auch mit copy-r und sync-r, jedoch nicht, wenn keine rekusive Option verwendet wird.

sticksync läuft normalerweise "silent" ab. Wird in 0_sync die Option "verbose:" oder "debug:" einzuschalten um die Synchronisation verfolgen zu können und/oder ggf. Fehler im Skript oder der 0_sync Datei zu finden. Im Beispiel ist "verbose" eingeschaltet und "debug" auskommentiert.

### in ~/daten/info/qi ###
stick:stick_emc
	sync-r:*.wri $STICK/info/qi/
	sync-r:*.dat $STICK/info/qi/
Beispiel 2 - einfaches Sync mit nur einem Stick

Aus diesem Verzeichnis werden nur die Dateien mit den Endungen "wri" und "dat" mit dem STICK "stick_emc" synchronisiert. Ich habe mir angewöhnt, den Pfad des Verzeichnisses als Kommentar in die 0_sync einzutragen. Erleichtert mir die Übersichtlichkeit, wenn ich im Editor "Geany" mehrere 0_snyc Dateien gleichzeitig offen habe.

### in ~/daten/Fotos ###
stick:HDD30G
    ### !!! be very carefully with --delete option
    copy-r:* $STICK/bilder/!--delete --exclude *.tif
Beispiel 3 - macht eine exakte Kopie von ~/daten/Fotos auf eine externe Festplatte, schließt dabei aber alle TIF-Dateien aus.

Durch die Verwendung der rsync Option "--delete" werden auf dem Zieldatenträger Dateien, die nicht im Sourceverzeichnis existieren, gelöscht. Auf der externen Festplatte wird somit eine exakte Kopie des Verzeichnisbaumes von "Fotos" gepflegt.

Die --delete Option ist nicht für Backups geeignet! Verwenden Sie die --delete Option nur wenn Sie wissen, was Sie tun.

Anweisungen von 0_sync

Die Anweisungen "test", "verbose" und "debug" gelten nur für das Verzeichnis, in dem sie in der 0_sync freigeschaltet sind.

zum Seitenanfang

Das Shell Skript sticksync

Das Skript kann einfach per copy and paste übernommen werden.
#!/bin/bash

###########################################################################
# Title      :    sticksync - backup, copy and sync files in User-HOME
# Author     :    Bernd Holzhauer 
# Date       :    2007-10-12
# Requires   :    rsync
# Category   :    File Utilities
###########################################################################
# Description
#    checks from user HOME recursively for 0_sync files and process them
#    the 0_sync should contain line starting with:
#    back: for backup
#    copy: for single way copy
#    sync: for dual way syncing files 
# Note:
#    - Uses different rsync parameters for VFAT and non VFAT Sticks/Disks
#    - The Handle filename (0_sync) may be changed to your personal taste
#    - Sticks should be mounted in $MOUNTDIR = /media
#    - $BACKDIR points to default backup BASEDIR.
###########################################################################

PN=`basename "$0"`        # Program name
VER='1.7'

##### Variables for customising the script #####
H_FILE="0_sync"
MOUNTDIR="/media"
BACKUPDIR="backup"

### --- Do NOT modify/edit below --- ###
Usage () {
    echo >&2 "$PN - Sync stuff to USB-Drive, $VER
usage: $PN [opt] [path]
       no options required
       -d runs in debug mode, gives a lot of output but don't sync
       -h to show a short help
       -v switch on verbose mode during processing
       -? will show this message

The programm searches the tree below User-Home-Dir for Job-Files named $H_FILE.
Concerning to the lines in this files it backup, copy or sync files using rsync.
If a path is applied, only the path and its subdirectories will be processed.
Example: sticksync -d test/ will debug only the 0_sync files in home/test/ and its surdirs.
"
    exit 1
}
Help () {
    echo >&2 "$PN - Sync local files to USB-Drive, $VER

$PN [opt] [path] - for possible options use $PN -?.
The programm searches the tree below User-Home-Dir for Job-Files named $H_FILE.
Concerning to the lines in this files it backup, copy or sync files using rsync.
A line beginnig with '#' is treated as comment.
There are 3 possible instructions: back, copy and sync. They are working just with
the directory where the $H_FILE file is placed. back-r, copy-r and sync-r are doing
the same but recursively thru the subdirectories, too.

Job file sample 1: $H_FILE in ~/.evolution
#debug:  # the commands are just viewed and a lot of output is generated to
#          check the line ... rsync is called with option -n = dryrun 
# This will backup ~/.evolution tree to ~/backup/.evolution
### stick:* = always do ###
stick:*
    ### be vary carefull with --delete option ###
    back-r:* !--delete

Job file sample 2: $H_FILE in ~/daten/info
#verbose: # enable this line will generate output while processing this dir
### The lines will be processed only if drive is mounted in /media/
#   and containing a file 'stick_emc' in its root dir 
stick:stick_emc
back:*.zkx
back-r:*.html !--exclude *.20*html
#one way copy instuctions
    copy:*.zkn \$STICK/data/
    copy-r:* \$STICK/all/!--exclude *.bak
# sync anweisungen
    sync:*.zkx \$STICK/info/
    sync-r:*.html \$STICK/info/ !--exclude *.20*html

### Begin other Stick ###
backupdir:\$STICK/backup/  # define (overwrite) Backup directory
stick:STICK1
    back-r:.              # make a backup including . = hidden files
    ..."
    exit 1
}
_verbose=0
_debug=0
_path="."
mounted=0

while [ $# -gt 0 ]
do
    case "$1" in
    -d)    _debug=1; _verbose=1;;
    -h)    Help;;
    -v)    _verbose=1;;
    -?)    Usage;;
    *)    break;;  # path
    esac
    shift
done

if [ "$1" != "" ]; then _path=$1; fi

if (( $_verbose )); then
	echo "*** sticksync Ver. $VER: processing path=$_path ***"
	if (( $_debug )); then echo "+++ debug mode is on"; fi
fi

cd        # process files in HOME dir
for i in `find $_path -name $H_FILE` ; do    # search for 0_sync files in HOME
    verbose=$_verbose
    debug=$_debug		
    #if (( $debug )); then echo "*********"; fi
    BACKDIR=$BACKUPDIR
    file=$i            # full file name
    path=${i%/*}       # remove filename from path
    if (( $verbose )); then echo "*** found $H_FILE in $path ***"; fi
    dir=${path##*/}
    if [ "$dir" = ".Trash" ]; then echo "    - skipping $file"; continue; fi # skip .Trash
    dir1=${path%/*}
    dir1=${dir1##*/}
    if [ "$dir1" = "$BACKDIR" ]; then echo "    - skipping $file"; continue; fi # skip $BACKDIR
    opt_e=""
    
    while read line; do        # process 0_sync file line by line
        if [ -z "$line" ]; then continue; fi # skip blank lines
        line=${line## }        # strip leading blanks from line
        if [ "${line:0:1}" = "#" ]; then continue; fi
        para=${line#*!}        # if no option(s) returns para = line
        if (( $verbose )); then echo "--> $line";fi
        if (( $debug )); then echo "+++ para:'$para'"; fi
         if [ "$para" = "$line" ]; then
            para=""                # unset para
        else
            line=${line%!*}        # strip parameter from line
        fi
        cmd=${line%:*}
        opt=${line#*:}
        opt=${opt## }        # strip leading blanks
        src=${opt%% *}
        src=${src%% }        # strip trailing blanks
        dest=${opt#* }
        dest=${dest%% }        # strip trailing blanks
        if [ "$src" = "*" ] || [ "$src" = "." ]; then
            if [ "$cmd" != "back" ] && [ "$cmd" != "back-r" ]; then para="$para --exclude $H_FILE"; fi
        fi
        if [ "$cmd" = "test" ]; then opt_e="n"; continue; fi
        if [ "$cmd" = "verbose" ]; then verbose=1; opt_e="${opt_e}v"; continue; fi
        if [ "$cmd" = "debug" ]; then verbose=1; debug=1; opt_e="vn"; continue; fi
        if [ "$cmd" = "backupdir" ]; then BACKDIR=$src; continue; fi
        if [ "$src" = "$dest" ]; then dest=""; fi
        if (( $debug )); then echo "+++ cmd='$cmd' - src='$src' - dest='$dest' - para='$para' opt='$optS$opt_e'"; fi
        if [ "$cmd" = "stick" ]; then
            for _mnt in `ls /media`; do
                if [ -f "$MOUNTDIR/$_mnt/$src" ] || [ "$src" = "*" ]; then
                    STICKDIR="$MOUNTDIR/$_mnt"
                    if (( $verbose )); then
                        if [ "$src" = "*" ]; then
                            echo "*** Stick ALL gefunden"
                        else
                            echo "*** Stick $STICKDIR/$src gefunden"
                        fi
                    fi
                    echo "    process $path for Stick: $src"
                    mounted=1
                    vfat=`mount | grep "$STICKDIR type vfat" | wc -l`
                    #echo "vfat=$vfat - $STICKDIR type vfat"
                    if (( vfat == 1)); then
                        opt_s="--modify-window=1 -tu -L"
                        opt_r="--modify-window=1 -rtu -L"
                    else
                        opt_s="-lptgou"
                        opt_r="-au"
                    fi
                    break
                else
                    mounted=0
                fi
            done
            continue
        fi
        ### if no valid stick mounted - continue reading file ###
        if (( ! $mounted )); then continue; fi
        if [ "${src:1:5}" = "STICK" ]; then src=${STICKDIR}${src%\$STICK}; fi
        if [ "${dest:1:5}" = "STICK" ]; then dest=${STICKDIR}${dest#\$STICK}; fi
        if [ "${BACKDIR:1:5}" = "STICK" ]; then BACKDIR=${STICKDIR}${BACKDIR#\$STICK}; fi
        if (( $debug )); then echo "+++ cmd='$cmd' - path='$path'"; fi
        ### set/modify some values for correct processing
        let len=${#dest}-1
        if [ $dest ] && [ "${dest:${len}:1}" != "/" ];then echo "Destination $dest '/' is missing"; exit; fi
        src_p=${src%/*}
        src_f=${src##*/}
        if [ "$src_p" = "src" ]; then src_p=""; fi
        if [ "$src_p" != "$src_f" ]; then src_p="${src_p}/"; fi
        if [ "${src_p:0:1}" = "*" ];then src_p=""; fi             # compare first char
        ### BACKUP ###        
        if [ "$cmd" = "back" ]; then
            cmd_back="rsync $opt_s$opt_e $para $path/$src $BACKDIR/$dir/"
            if (( $verbose )); then echo "--> $cmd_back"; fi
            $cmd_back
        fi
        if [ "$cmd" = "back-r" ]; then
            cmd_back="rsync $opt_r$opt_e $para $path/$src $BACKDIR/$dir/"
            if (( $verbose )); then echo "--> $cmd_back"; fi
            $cmd_back
        fi
        ### One Way Copy ###
        if [ "$cmd" = "copy" ]; then
            cmd_copy="rsync $opt_s$opt_e $para $path/$src $dest"
            if (( $verbose )); then echo "--> $cmd_copy"; fi
            $cmd_copy
        fi
        if [ "$cmd" = "copy-r" ]; then
            cmd_copy="rsync $opt_r$opt_e $para $path/$src $dest"
            if (( $verbose )); then echo "--> $cmd_copy"; fi
            $cmd_copy
        fi
        ### Sync both ways ###
        if [ "$cmd" = "sync" ]; then
            cmd_sync="rsync $opt_s$opt_e $para $path/$src $dest"
            if (( $verbose )); then echo "--> $cmd_sync"; fi
            $cmd_sync
            cmd_sync="rsync $opt_s$opt_e $para $dest/$src_f $path/$src_p"
            if (( $verbose )); then echo "--> $cmd_sync"; fi
            $cmd_sync
        fi
        if [ "$cmd" = "sync-r" ]; then
            cmd_sync="rsync $opt_r$opt_e $para $path/$src $dest"
            if (( $verbose )); then echo "--> $cmd_sync"; fi
            $cmd_sync
            cmd_sync="rsync $opt_r$opt_e $para $dest$src_f $path/$src_p"
            if (( $verbose )); then echo "--> $cmd_sync"; fi
            $cmd_sync
        fi
    done < $file
    if (( $verbose )); then echo "*********"; fi
    
done
cd - >/dev/null

exit
Listing: sticksync Script © Bernd Holzhauer

Achtung: Testen Sie Ihre 0_sync Dateien vorsichtig und gründlich. Für die Sicherung Ihrer Daten und für eventuelle Datenverluste sind Sie selbst verantwortlich. Ich lehne jede Verantwortung für eventuelle Datenbeschädigungen ab.

zum Seitenanfang




Home Page
Ranking-Hits
Copyright © 2001-2014 Bernd Holzhauer. Alle Rechte vorbehalten.
Warenzeichen und Marken sind Eigentum der jeweiligen Besitzer.

Das Ing.Büro Bernd Holzhauer distanziert sich grundsätzlich von gesetzeswidrigen und möglicherweise illegalen Inhalten in Seiten, auf die über www.cc-c.de verwiesen wird. Sollte Ihnen diesbezüglich etwas auffallen, melden Sie es uns bitte per email.