/ HomePage / Computer / Software / Programmierung / Massenverarbeitung
Scripte für die Massenverarbeitung
Eigentlich sind Scripte für die wiederkehrende Verarbeitung prädestiniert. Wer einmal ein Bild mit Gimp skaliert hat, weiß was das für ein Aufwand ist. Bild öffnen, Bild skalieren, Bild speichern. Das mag sich nicht kompliziert anhören, ist es eigentlich auch nicht, nur, wenn man das dann 10x machen soll wird es schnell langwierig und fehleranfällig, da immer wieder die selben Schritte vorgenommen werden sollen. Ok, für nur 10x kann man es eben noch machen, aber sobald man es irgendwann nochmal machen soll... ist es stupide Arbeit.
Voraussetzungen
Ein Script, oder ein Programm das die Arbeit erledigt. Zum Bearbeiten von Bildern per Script bietet sich ImageMagick an.
convert <Datei> -scale 320x <Neue_Datei>
Ein Script zur Bildskalierung genau einer Datei
Es bleibt also nur die Verarbeitung des Dateinamens.
#!/bin/bash
# Prüfen, ob ein Parameter übergeben wurde
if [ $# -eq 0 ]; then
echo "Usage: $0 <Datei>"
exit 1
fi
if [ ! -e "$1" ]; then
echo "Datei existiert nicht"
exit 1
fi
# Datei zerlegen in dessen eigentlichen Namen und dem Pfad dazu
BASENAME=$(basename $1)
DIRNAME=$(dirname $1)
NEWNAME="img_320x_${BASENAME}"
NEWPATH=$DIRNAME/$NEWNAME
if [ -e "$NEWPATH" ]; then
echo "Neue Datei existiert bereits"
exit 1
fi
# hier passiert die Umwandlung
convert $1 -scale 320x $NEWPATH
Zu speichern als resizeTo320x
Damit können wir jetzt exakt ein Bild in einem beliebigen angegebenen Pfad in 320x wandeln. Die neue Datei fängt immer mit 'img_320x_' an. Es gibt hier zusätzlich ein paar Sicherheitsabfragen, nur um mal zu zeigen wie man das machen könnte.
Script mehrfach wieder verwenden
Wir wollen die resizeTo320x jetzt aber mehrfach aufrufen.
find . -name '*.jpg' -exec /.../resizeTo320x {} \;
Das tippt man eben so runter.
Nachteile
- Es werden nur .jpg berücksichtigt, -iname '*.jpg' ist Case-insensitiv.
- Es werden nur Bilder vom Type JPEG berücksichtigt
find . -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.bmp' -o -iname '*.gif' \
-o -iname '*.png' -o -iname '*.tif' -o -iname '*.tiff' -exec /.../resizeTo320x {} \;
Das tippt niemand mehr.
Q: Aber wie die beiden Scripte miteinander verbinden?
A: Pipe!
Liste erstellen
Also erstellen wir ein Script, was sämtliche Bilder findet.
find . -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.bmp' -o -iname '*.gif' \
-o -iname '*.png' -o -iname '*.tif' -o -iname '*.tiff'
Speichern als listpics
Damit kann man alle Bilder von Typ jpg, jpeg, bmp, gif, png, tif, tiff von der aktuellen Pfad-Position finden, die Namen werden auf der Konsole (Standard out) ausgegeben. Ggf. erweitern.
Das funktioniert so allerdings nicht, da resizeTo320x nur mit Parametern arbeitet und nicht mit Übergaben aus dem Standard Input.
Standard Input zu Parameteraufruf wandeln
Man könnte jetzt die resizeTo320x dahingehend erweitern, das sie beides kann, mit Parametern umgehen und mit Standard Input zurecht kommt, da resizeTo320x aber nur ein Beispiel sein soll, muss eine andere Lösung her.
Warum nicht ein weiteres Script, das uns diese Arbeit abnimmt. Also alle per Standard Input ankommenden Parameter einfach in Parameter umsetzt und unser Script damit aufruft.
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Caller: which program should be called?"
exit 1
fi
# echo "external program: $1"
CALLER="$1"
shift
if [ $# -eq 0 ]; then
# rufe übergebenes Program mit jeden Parameter der per Standard Input reinkommt
read parameter
while [ -n "${parameter}" ]; do
# echo ${CALLER} $parameter
${CALLER} $parameter
shift
read parameter
done
else
# übergebe alle Parameter einzeln an das übergebene Program
while [ $# -ne 0 ]; do
${CALLER} $1
shift
done
fi
Speichern als caller
Nun können wir auf der einen Seite Listenausgaben (fast) direkt an andere Scripte übergeben. Ohne uns in jedem Script Gedanken darüber zu machen, wie man es mehrfach anwendet.
listpics | caller resizeTo320x
listpics findet beliebig viele Bilder und übergibt diese an den caller der für jeden Standard Input unser Programm resizeTo320x aufruft. Wir brauchten das resizeTo320x nicht mehr anpassen, um mit einem Parameter oder mit Standard Input zurechtzukommen. Der Stapelverarbeitung steht nichts mehr im Weg...
Fazit: Gibt es doch schon mittels xargs
Ok ok, gibt es schon. Das hier beschriebene Verfahren ist auch nur mal ein Beispiel, wie man es machen könnte und so etwas gibt es auch schon, wer sich mit Stapelverarbeitung auskennt, weiß dass es dafür xargs gibt.
listpics | xargs -n 1 resizeTo320x
Der xargs Parameter -n 1 besagt, das unser Programm resizeTo320x für jeden Parameter explizit aufgerufen wird, sonst würden solange Parameter übergeben wie die Shell hergibt (bis zu 32000 Zeichen)
- (ohne -n 1) resizeTo320x bild1 bild2 bild3 ... bildN
- (mit -n 1) resizeTo320x bild1 ; resizeTo320x bild2 ...
Der caller soll ja auch nur mal aufzeigen, wie xargs intern funktioniert.
DNG to JPG Batch Konvertierung
Ich konvertiere alle meine Bilder von den propritären Raw Formaten der Kamarahersteller *.cr2, *.nef, etc. immer in das Adobe dng Format. So nutze ich ein einheitliches und offenes Format, um alle Bilder zu archivieren. Dennoch kommt es vor, dass man die Bilder an andere Personen weitergeben möchte. Dafür eignet sich das jpg Format deutlich besser. Um ein ganzes Verzeichnis von dng zu jpg zu konvertieren, habe ich mir folgendes batch geschrieben.
Vorraussetzung sind imagemagick und unf ufraw. Unter Ubuntu zu installieren mit:
sudo apt-get install imagemagick ufraw
#!/bin/bash
for f in *.dng
do
BASENAME=$(basename $f .dng)
NEWNAME=$BASENAME.jpg
convert -quality 85% $f $NEWNAME
done
Dateien mit gewissen Schema kopieren
find . -iname '*schema*' -print0 | xargs -0 -I MYFILE cp MYFILE /tmp/destinationdir
Kopiert mal eben sämtliche Dateien, egal in welchem Unterverzeichnis die '*schema*' im Namen haben nach /tmp/destinationdir. Ist doch eigentlich ganz einfach.
Links