Artikel des Monats Februar 2009

Dateigröße 16 Bit TIFF ZIP-komprimiert und unkomprimiert

Samstag, 28. Februar 2009

Heute ließ ich meine 16 Bit TIFF-Bilder, die ZIP-komprimiert waren, konvertieren in unkomprimierte durch die Photoshop CS2-Stapelverarbeitung. Anschließend verglich ich die Dateigröße: 430 Bilder belegten komprimiert 20 Gigabyte und unkomprimiert etwas weniger.

Fazit: Ich werde (auch) zukünftig 16 Bit TIFF-Bilder unkomprimiert speichern. Das ist schneller und bereitet weniger Probleme: Einige Programme können ZIP-komprimierte TIFF-Bilder nicht lesen, zumindest die convert-Windows-Version (ImageMagick 6.4.9). Die unkomprimierten Bilder benötigen eher weniger Platz auf der Festplatte.

Daten sichern mit rsync

Dienstag, 24. Februar 2009

Täglich sichert das Programm rsync automatisch neue und veränderte Dateien verschiedener Verzeichnisse auf eine externe Festplatte.

Wichtige Dateien, wie jene von Programmier-Projekten, überführe ich in eine Versions-Verwaltung, so benötige ich keine Backup-Versionen. Habe ich eine Datei gelöscht von den zu sichernden Verzeichnissen, wird diese von der externen Backup-Festplatte ebenfalls gelöscht: Auf der Backup-Festplatte sind Spiegel der gesicherten Verzeichnisse.

Zum Sichern schrieb ich ein Bash-Skript. Es liest aus einer Konfigurationsdatei die Namen der Quell- und Zielverzeichnisse und die Namen von Dateien mit Mustern von Dateien, die nicht gesichert werden sollen, beispielsweise Temporärverzeichnisse oder Cache-Verzeichnisse. Das Skript:

#!/bin/sh
#
# Author  : Elmar Baumann
# Date    : 2006/05/05
# Requires: rsync, Konfigurationsdatei $CONF
# Doc     : Synchronisiert Dateien auf ein externes Backupmedium
#
#           Aufruf ohne Parameter
#
###############################################################################

SCRIPT=$(basename $0)

# Mountpoint für externes Backupmedium
BACKUP_MOUNTPOINT=/mnt/backup

# Konfigurationsdatei
CONF=${HOME}/conf/backup-directories.conf

# Spalte in der Konfigurationsdatei mit Quellverzeichnissen
COLUMN_SOURCE=1

# Spalte in der Konfigurationsdatei mit Zielverzeichnissen
COLUMN_TARGET=2

# Spalte in der Konfigurationsdatei mit Exclude-Pattern-Dateien
COLUMN_EXCLUDE=3

# Logdatei
LOGFILE=${HOME}/log/${SCRIPT}.txt

# Zeilenumbruch als Fieldseparator
IFS="
"

# Optionen für rsync
RSYNC_OPTIONS="
--archive
--delete
--delete-excluded
"

###############################################################################

function print_date() {
    date +"%d.%m.%Y, %H:%M:%S Uhr"
}

function print_started() {
    echo "${SCRIPT}: $(print_date) Sichere Dateien auf '${BACKUP_MOUNTPOINT}'..." \
    | tee $LOGFILE
}

function print_finished() {
    echo "${SCRIPT}: $(print_date) Fertig. Logdatei: '${LOGFILE}'" \
    | tee -a $LOGFILE
}

function mount_backup_medium() {
    umount $BACKUP_MOUNTPOINT 2> /dev/null
    mount $BACKUP_MOUNTPOINT
    if [ $? != 0 ]
    then
        echo "${SCRIPT}: '${BACKUP_MOUNTPOINT}' laesst sich nicht einhaengen!" >&2
        exit 1
    fi
}

function umount_backup_medium() {
    sync
    umount $BACKUP_MOUNTPOINT
}

function trim() {
    echo $1 | sed -e 's/^ *//;s/ *$//'
}

function shrink_blanks {
    echo $1 | sed 's/  */ /g'
}

function get_column_of_line() {
    local line=$(shrink_blanks $1)
    local column=$2

    trim $(echo $line | cut -d " " -f $column)
}

function backup() {
    for line in $(cat $CONF | grep -v '^#.*')
    do
        source=$(get_column_of_line $line $COLUMN_SOURCE)
        target=$(get_column_of_line $line $COLUMN_TARGET)
        exclude=$(get_column_of_line $line $COLUMN_EXCLUDE)

        rsync $RSYNC_OPTIONS \
            --exclude-from=$exclude \
            $source \
            $target \
            2>&1 \
            | tee -a $LOGFILE
    done
}

###############################################################################

print_started
mount_backup_medium
backup
umount_backup_medium
print_finished

Die Konfigurationsdatei sieht so aus — zwischen den Verzeichnis- und Dateinamen stehen Leerzeichen:

# Quelle         Ziel                   Patterndatei für Ausschluss
#
/home/elmar      /mnt/backup/linuxhome  /home/elmar/conf/backup-exclude-patterns-home.conf
/mnt/bilder      /mnt/backup            /home/elmar/conf/backup-exclude-patterns-bilder.conf
/mnt/daten       /mnt/backup            /home/elmar/conf/backup-exclude-patterns-daten.conf
/mnt/doku        /mnt/backup            /home/elmar/conf/backup-exclude-patterns-doku.conf
/mnt/multimedia  /mnt/backup            /home/elmar/conf/backup-exclude-patterns-multimedia.conf

Ich lasse rsync nicht alle gesicherten Dateien ausgeben, in der langen Ausgabe übersähe ich Fehlermeldungen, beispielsweise Dateien, die nicht gelesen, geschrieben oder gelöscht werden können.

Die externe Backup-Festplatte ist nur während des Sicherns eingeschaltet, ich sollte nach dem Ausschalten die Kabel herausziehen, so beschädigt eine hohe Überspannung nicht die Festplatte.

Das Skript starte ich durch Aufruf, nicht automatisch beim Einschalten der Backup-Festplatte, diese könnte ich einschalten, veränderte oder gelöschte Dateien wieder herzustellen. Ich gebe den Alias ‘s’ ein und drücke die Enter-Taste.

Jedes Jahr brenne ich die Daten auf zwei Sätze DVDs gleichen Inhalts und guter Qualität von verschiedenen Herstellern, aktuell einen Satz auf Verbatim Archival Grade, den anderen auf Plextor DVD+R, hergestellt von Taiyo Yuden. Der Brenner ist auf 4-fache Geschwindigkeit eingestellt anstelle 16-facher. Die DVDs beider Sätze stecken in Hängeregister-DVD-Hüllen lichtdichter Koffer, ein Satz lagert außer Haus. Die Dateinamen jeder DVD stehen auf der Festplatte in einer komprimierten Textdatei des gleichen Namens wie das DVD-Label.

Zum vollständigen Rücksichern sind im Skript die Variablen target und source zu vertauschen – vielleicht erweitere ich es um diese Option: Das Skript wird bei gesetzter Option Spalte 2 der Konfigurationsdatei für source auswählen und die Spalte 1 für target: COLUMN_SOURCE=2 und COLUMN_TARGET=1.

Fehler behandeln: Automatisierte Bash-Skripte

Samstag, 21. Februar 2009

Laufen Skripte automatisch ab, will ich wissen von Fehlern, die auftraten. Ich habe keine Lust, die Logdateien zu durchsuchen, sondern lasse ich mir im Falle eines Fehlers eine E-Mail schicken.

Im Artikel MySQL-Datenbanken sichern schrieb ich, wie ich automatisch meine Datenbanken im Web sichern lasse. Die auf dem Webserver gespeicherten Backup-Dateien werden täglich automatisch lokal gespiegelt. Dazu schrieb ich ein Skript, das mit Hilfe des Perl-Skripts mirror Verzeichnisse anderer Rechner lokal spiegelt und das von mehreren Skripten aufgerufen wird:

#!/bin/sh
#
# Author  : Elmar Baumann
# Date    : 2008/12/21
# Requires: Paket mirror (Perl-Skript)
# Doc     : Spiegelt lokal eine Verzeichnishierarchie eines anderen Rechners
#
#           Aufruf: mirror.sh <Konfiguratiosdatei für mirror>
#
################################################################################

# Konfigurationsdatei für mirror
CONF=$1

# Logdatei, in der die Ausgaben von mirror stehen und die an Fehler-E-Mails
# angehängt werden
ERROR_LOGFILE="/tmp/log/$(basename $0)-errors-$$.txt"

# Absender der E-Mail bei Fehlern
ERROR_MAIL_FROM="$(basename $0)"

# Betreff der E-Mail bei Fehlern
ERROR_MAIL_SUBJECT='Fehler beim Spiegeln'

# Empfänger der E-Mail bei Fehlern
ERROR_MAIL_TO='elmar'

# Inhalt der E-Mail bei Fehlern
ERROR_MAIL_MSG='Fehler beim Spiegeln, siehe angehaengte Logdatei.'

################################################################################

function error_mail() {
    echo "${ERROR_MAIL_MSG}" | /usr/bin/mail \
        -a "${ERROR_LOGFILE}" \
        -r "${ERROR_MAIL_FROM}" \
        -s "${ERROR_MAIL_SUBJECT}" \
        "${ERROR_MAIL_TO}"
}

################################################################################

/usr/bin/mirror "${CONF}" >& "${ERROR_LOGFILE}" || error_mail

rm -f "${ERROR_LOGFILE}"

Meine MySQL-Datenbanken im Web kann ich mit folgendem Befehl lokal sichern:

$HOME/bin/mirror.sh $HOME/conf/mirror_website_dbs.conf

Da ich etliche Mirror-Skripte habe, gehe ich anders vor: Im Konfigurationsverzeichnis heißen alle Konfigurationsdateien für mirror mirror_*.conf. Will ich weitere Dateien spiegeln, reicht es, eine neue Datei dieses Namensmusters im Konfigurationsverzeichnis anzulegen. Code des Skripts:

#!/bin/sh

for config_file in /home/elmar/conf/mirror_*\.conf
do
    /home/elmar/bin/mirror.sh $config_file
done

Die Pfade sind ausgeschrieben: Mein Skriptverzeichnis ist nicht in crons Pfad und das Verzeichnis $HOME ist für cron ein anderes als meines.

Ich teste automatisierte Skripte, ob sie mir Fehler-E-Mails schicken: Ich provoziere Fehler, in diesem Beispiel durch falsche Angaben in der Konfigurationsdatei für das Perl-Skript mirror.

Mein privater “Organizer”

Freitag, 20. Februar 2009

Was ich mir privat merken will, schreibe ich am Computer auf KNotes. Ich hole es in den Vordergrund durch Klick auf sein Icon im Tray der Taskleiste. Benötige ich es nicht mehr, versteckt es sich. Was ich eingebe, speichert KNotes ohne mein Hinzutun. Habe ich das Aufgeschriebene erledigt oder ist es nicht mehr aktuell, lösche ich es. Sonst nutze ich keine weiteren Funktionen von KNotes.

Ausnahmen: Mein Geburtstagsprogramm erinnert mich an Geburtstage, und Termine, an die ich erinnert werden will, trage ich ein in den Kalender Lightning.

KNotes

KNotes

In Texten nichts sagende Wörter vermeiden

Sonntag, 15. Februar 2009

Streiche ich aus Texten Wörter und Phrasen, die nichts sagen, sind sie leichter zu verstehen. Vor Jahren schrieb ich nichts sagende Wörter in eine Textdatei, die seitdem wartet, benutzt zu werden. Gestern programmierte ich ein Perl-Skript, das mir anzeigt, ob bestimmte Wörter in einem Text stehen und werde es zukünftig nutzen, meine Artikel auf  nichts sagende Wörter zu überprüfen.

Das Skript liest Wörter aus einer Datei oder von der Standardeingabe. Letzteres ist erforderlich für Texte, die nicht aus einer Datei gelesen werden können, beispielsweise eine externe Webseite, und wenn der Text vorher von Formatierungen befreit werden soll, wie das zutrifft für HTML-Dateien und für alle Binärformate wie PDF- oder Word-Dateien.

Sie können das Perl-Skript herunterladen. In nächster Zeit werde ich die Texte meiner Fotografie-Informationen überarbeiten. Die betroffenen Dateien und Wörter ließ ich in eine Textdatei ausgeben; den Befehl, den ich in die Bash eingab, breche ich hier um, so ist er übersichtlicher:

for i in $(find -name '*html' | sort)
do
         lynx --dump $i \
         | findwords.pl \
                 -c  \
                 -l negativ-wortliste.txt \
                 -s
         if [ $? -eq 0 ]
         then
                 echo -n "$i: "
                 lynx \
                          --dump \
                          --hiddenlinks=ignore \
                          --nonumbers $i \
                 | findwords.pl \
                          -c  \
                          -l negativ-wortliste.txt
         fi
done \
| tee -a $HOME/log/negativ_words.txt

Anschließend gab ich die Dateinamen aus in Dateien zu je 100 Zeilen. Mit einem Editor kann ich so auf einmal 100 Dateien laden und bearbeiten: editor $(cat files-aa)&

grep '^\./' $HOME/log/negativ_words.txt \
| cut -d: -f1 \
| sed 's%^\./%%' \
| sort \
| uniq  \
| split -l 100 - files-

In meine Editoren habe ich einen Befehl eingebaut, der den Text weitergibt an das Skript.

Im Perl-Skript benutze ich die Perl-Module Util::String.pm und Util::System.pm, die ich für wiederkehrende Aufgaben schrieb. Hier der Code beider aufgerufenen Funktionen:

use File::Basename;

sub basename {
    return File::Basename::basename($0);
}
sub trim {
    my ($string_ref) = @_;

    ${$string_ref} =~ s/^\s+//;
    ${$string_ref} =~ s/\s+$//;
}

Die Negativwortliste entnahm ich dem Buch: E. A. Rauter: Die neue Schule des Schreibens, Econ, Düsseldorf 1996. Sie ist zukünftig zu erweitern.

Als Erweiterung sehe ich eine Option vor: Es müssen nicht ganze Wörter übereinstimmen, Teilzeichenketten reichen auch.

Einen Wunsch habe ich noch: Eine umfangreiche Liste mit Adjektiven und Attributen für mein Skript. Deren Notwendigkeit in Texten ist zu überprüfen.

Nachtrag: Ich eröffnete ein Projekt, das Wörter einer Wortliste in einem Text farbig hervorhebt.

Anwendungsfenster des Projekts Word Highlighter