LaTeX – Grafiken einbinden, Tabellen basteln, Referenzen innerhalb und außerhalb des Dokumentes, Präsentationen mit Beamer und Sonstige Kleinigkeiten

Juni 22, 2013 um 5:28 vormittags | Veröffentlicht in LaTeX, Programmieren, Ubuntuusers | 1 Kommentar

Inzwischen ist der Workshop abgeschlossen. Die beiden letzten Termine (4.6. und 18.6.) lagen so nah bei einander, dass ich unter den Vorbereitungen keine Zeit hatte die Dateien zu veröffentlichen. Daher fasse ich die Veröffentlichungen gleich in einem Artikel zusammen.

Der erste Workshop befasste sich mit Grafiken, Tabellen und Referenzen. Im Zweiten ging es dann um Präsentation. Im Anschluss wurden noch ein paar zusätzliche Formatierungsmöglichkeiten und technische Details gezeigt.

Dateien

Grafiken.tex   <– TeX-File für Grafiken
Grafiken.pdf   <– Kompiliertes PDF
Tabellen.tex   <– TeX-File für Tabellen
Tabellen.pdf   <– Kompiliertes PDF
Referenzen.tex   <– TeX-File für Referenzen
Referenzen.pdf   <– Kompiliertes PDF

Presentation.tex   <– TeX-File für Präsentation
Presentation.pdf   <– Kompiliertes PDF
Sonstiges.tex   <– TeX-File zu sonstigen Themen
Sonstiges.pdf   <– Kompiliertes PDF

Einheit01.bib   <– Das zugehörige Bib-File (Zitiermaterial) (update)
include.tex   <– Header-File mit allen notwendigen Layout-Informationen und eingebundenen Paketen (update)
cc-by-sa.png   <– CC-Grafik

Für Presentation.tex

Taach.png  <– Ja, ich habe das Logo des Blogs als Beispiel verwendet
Taach.jpeg  <– Selbes Bild, anderes Dateiformat

Für Sonstiges.tex

Farbennamen.png  <– Ein Haufen vordefinierter Farben
geraetetreiber1.png  <– Verschiedene Gerätetreiber
geraetetreiber2.png  <– Verschiedene Gerätetreiber
geraetetreiber3.png  <– Verschiedene Gerätetreiber

Anmerkung:  Das Original aus dem Workshop hat auf der Titelfolie noch eine zusätzliche Grafik: das Logo des Mentoring-Programms der Uni Wien. Da ich dieses nicht unter eine CC-Lizenz  stellen kann habe ich es kurzerhand herausgenommen. Außerdem habe ich den Header noch nicht aufgeräumt. Hier ist ein Haufen an neuen Kommandos, welche keine logische Struktur haben. All das in Version 2.0 :)

Wie auch im PDF geschrieben: alle diese Dateien stehen unter einer CC-BY-SA 3.0, mit einer Ausnahme: die cc-by-sa.png-Grafik, an welcher alleine Creative Commons die Rechte hält.

=-=-=-=-=
Powered by Blogilo

LaTeX – Mathe-Modus und BibTex

Mai 8, 2013 um 6:09 vormittags | Veröffentlicht in LaTeX, Ubuntuusers | 10 Kommentare

Wie ich schon vor längerer Zeit geschrieben habe, halte ich in diesem Semester, zusammen mit zwei Kollegen, am Informatik-Institut der Uni Wien einen LaTeX-Workshop ab. Dieses Mal waren die Themen, die wir vorgetragen haben Mathematik-Modus (Formelsetzen) und BibTeX (Zitieren).

Selbstverständlich ist auch diesen Folien der Umfang dieses Teilbereiches von LaTeX nicht erschöpft, sie sollen nur einen Punkt zum Einstieg bieten.

Beim nächsten Termin (4.6.) wird es um Tabellen erstellen, Grafiken einbinden und Referenzen setzen (innerhalb und außerhalb des Dokuments) gehen.

Dateien

Mathe-Modus.tex  <– TeX-File für den Mathe-Modus
Mathe-Modus.pdf  <– Kompiliertes PDF
BibTeX.tex  <– TeX-File für BibTex
BibTeX.pdf  <– Kompiliertes PDF

Einheit01.bib  <– Das zugehörige Bib-File (Zitiermaterial)
include.tex  <– Header-File mit allen notwendigen Layout-Informationen und eingebundenen Paketen
cc-by-sa.png  <– CC-Grafik

Anmerkung: Das Original aus dem Workshop hat auf der Titelfolie noch eine zusätzliche Grafik: das Logo des Mentoring-Programms der Uni Wien. Da ich dieses nicht unter eine CC-Lizenz  stellen kann habe ich es kurzerhand herausgenommen. Außerdem habe ich den Header noch nicht aufgeräumt. Hier ist ein Haufen an neuen Kommandos, welche keine logische Struktur haben. All das in Version 2.0 :)

Wie auch im PDF geschrieben: alle diese Dateien stehen unter einer CC-BY-SA 3.0, mit einer Ausnahme: die cc-by-sa.png-Grafik, an welcher alleine Creative Commons die Rechte hält.

=-=-=-=-=
Powered by Blogilo

Horner – Konvertieren zwischen Zahlensystemen

April 8, 2013 um 7:25 nachmittags | Veröffentlicht in Bash, Free Software/Open Source, Programmieren, Ubuntuusers | 10 Kommentare

“Mama, weißt du wie ich von einem Zahlensystem in ein anderes umrechne?”

“Nein, Schätzchen. Weiß Linux es nicht?”

“Nein, ich kann kein Programm finden, dass das kann.”

“Es gibt kein Programm um zwischen Zahlensystemen zu Konvertieren? Oh, nein …”

*Türe wird aufgestoßen, Horner-man stürmt herein*

“Fürchtet euch nicht, gesetzestreue Linuxer! Eure Klagen wurden erhöhrt!”

“Ooh, Horner-man. Unser Held!”

Autsch. Das tut weh. Ich habe es selbst geschrieben und es schmerzt trotzdem sehr. Abgegriffenes Szenario, unglaubwürdige Darsteller, ein Hauch von Sexismus. Autsch.

Kommen wir zum Thema. Seit Langem wollte ich ein Programm haben, mit dem ich einen Wert in verschiedenen Zahlensystemen darstellen kann. Dazu musste dieses Programm jedoch die Umrechnung zwischen diesen Systemen beherrschen. Mein vorrangiges Ziel war die Umrechnung vom Dezimalsystem ins Binärsystem und umgekehrt. Es gibt sicherlich Programme da draußen, die dafür geschrieben sind oder es zumindest nebenbei beherrschen. Speziell die Konvertierung zwischen Zahlensystemen zur Basis 2, 8, 10 und 16 findet sich häufig in digitalen Taschenrechnern. Speedcrunch ist ein schönes Beispiel dafür. Allerdings wollte ich nicht einfach nur ein Programm, welches mir ein Ergebnis anzeigt, sondern auch die Möglichkeit dieses Programm in einem Script zu verwenden. Es musste also konsolenbasiert arbeiten und Ein- und Ausgabe sollten über die Standardkanäle erfolgen.

Da ich kein entsprechendes Programm gefunden habe, habe ich mir vorgenommen selbst eines zu schreiben. Da ich außerdem schon länger das Horner-Schema testweise implementieren wollte,  entschied ich mich dazu, die Umrechnung damit vorzunehmen. Dazu muss erwähnt werden: ich habe dieses Programm für meine persönlichen Bedürfnisse entwickelt, weswegen es nur mit ganzen Zahlen umgehen kann. Die Berechnung mit Gleitkommazahlen ist um einiges komplizierter und es braucht ein bisschen Hirnschmalz um ein Programm zu entwickeln, das nicht sofort Probleme mit der Rechengenauigkeit bekommt. Vielleicht erweitere ich das Programm irgendwann in diese Richtung. Vorerst kann es nur Integer verwerten. Dafür kann es mit Zahlen verschiedenen Basen arbeiten. Die Untergrenze ist Basis 2 (Binär), die Obergrenze ist 36. Rein theoretisch könnte es natürlich noch mehr Basen verarbeiten, bei 36 gehen mir jedoch die sinnvollen Zahlenrepräsentationen aus (0-9, a-z).

Das Programm, oder mehr die Befehlssammlung, kann positive und negative Zahlen von 0 bis 4294967295 verwerten. Dabei ist jedoch 4294967295 das Minimum an Obergrenze. Auf einem 64-bit System ist die Obergrenze 18446744073709551615 (abhängig vom Compiler; hier: GCC). Installation und Verwendung werden noch beschrieben. Zuerst jedoch eine kleine Exkursion:

Was ist das Horner-Schema?

Das Horner-Schema wurde von William George Horner entwickelt und dient der Polynomberechnung. Da die Umrechnung in ein anderes Zahlensystem als Polynom dargestellt werden kann ist das Horner Schema (oder Horner’s Method im Englischen) eine sehr einfache und schnelle Art der Berechnung. Im Großen und Ganzen funktioniert es so:

Umwandlung in das Dezimalsystem; B ist die Basis, a ist die Ausgangszahl, a1 die erste Stelle derselben, …:
(((a1*B + a2)*B + a3)*B + a4) ...

Bsp:
Wir wollen den Binärwert (Basis = 2) 101010 in das Dezimalsystem umwandeln:
((((1*2+0)*2+1)*2+0)*2+1)*2+0 = 42

Die Umwandlung eines Dezimalwertes in ein anderes System funktioniert analog dazu. Zuerst wird per Modulo (Restwertdivision) der Restwert errechnet. Dieser stellt bereits eine Stelle des Ergebnisses dar. Da dieser Rechenvorgang genau umgekehrt zur Umwandlung ins Dezimalsystem verläuft, ist auch das Ergebnis umgekehrt:
42   % 2 = 0
42-0 / 2 = 21
21   % 2 = 1
21-1 / 2 = 10
10   % 2 = 0
10-0 / 2 = 5
 5   % 2 = 1
 5-1 / 2 = 2
 2   % 2 = 0
 2-0 / 2 = 1
 1   % 2 = 1
 1-1 / 2 = 0

Sobald die Zahl, mit der man rechnet 0 geworden ist, endet die Rechnung. Die fettgedruckten Zahlen ergeben, von unten nach oben gelesen, das Ergebnis. Die Zeichenkette ist also 101010. Das ist dieselbe Zeichenkette, die wir zuvor ins Dezimalsystem umgewandelt haben. Es funktioniert!

Die Programme

Ich habe für jeden Rechenvorgang ein C++-Programm geschrieben. 2dec wandelt eine Zahl in das Dezimalsystem um, dec2 wandelt eine Dezimalzahl in eine beliebige Basis (von 2 bis 36) um. Die beiden Programme können jedes für sich aufgerufen werden mit:
dec2 <Basis> <Wert>
2dec <Basis> <Wert>

Die <Basis> ist immer die Basis aus der, bzw. in die ich umrechnen möchte. Die jeweils andere Basis ist ja das Dezimalsystem.

Möchte ich nun den Wert 42 in das Binärsystem umrechnen, so rufe ich das Programm dec2 auf:
dec2 2 42

Möchte ich die Berechnung umdrehen, so verwende ich das Programm 2dec:
2dec 2 101010

Die beiden Programme lassen sich kombinieren, wenn ich von einer Basis in eine andere konvertieren möchte und keine von beiden die Basis 10 ist. Möchte ich wissen, welchen Binärwert der Hexadezimale Ausdruck affe hat, dann mache ich das so:
dec2 2 $(2dec 16 affe)

Ergebnis: 1010111111111110

Das Script

Diese Kombination übernimmt das Script horner. Dieses übernimmt per -i die Basis des Inputs und per -o die Basis, in die der Wert umgewandelt werden soll (Output):
horner -i 16 -o 2 affe
entspricht der obigen Kombination der beiden Programme. Das Script geht davon aus, dass die beiden Programme in einem Verzeichnis abgelegt sind, das in der $PATH-Variablen des Nutzers eingetragen ist.

Das Makefile

Am Besten ist es, wenn man den Code selbst kompiliert. Für all diejenigen, die sich dabei unwohl fühlen oder es einfach noch nicht gemacht haben: keine Sorge, das Makefile übernimmt diese Arbeit. Dazu muss das Paket automake im System installiert sein.

Installation

Zuerst benötigt man den Code, den es hier zum herunterladen gibt.

Dieses Archiv lässt sich per
tar -xvzf Horner.tar.gz
ins aktuelle Verzeichnis entpacken. Die entpackten Dateien liegen dann im selben Verzeichnis wie das Archiv.

Sobald man die Dateien entpackt hat muss man nur in einem Terminal
make
aufrufen. Dies kompiliert den Code und erzeugt die beiden Programme 2dec und dec2.

Mittels
make install
kann man die Programme dann automatisch installieren. Dabei werden sie im Verzeichnis /bin im Home-Verzeichnis des Nutzers abgelegt. Dieses Verzeichnis sollte immer im $PATH liegen und der Nutzer hat dort immer Schreibrechte. Möchtest du die Installation anpassen (sprich: die Dateien woanders unterbringen), dann musst du nur die Dateien 2dec, dec2 und horner im gewünschten Verzeichnis ablegen.

Abschlussbemerkung

Die Programme, das Script und das Makefile stehen unter der GPLv3. Sie sind in keinster Art und Weise getestet und implementieren so gut wie keine Fehlerbehandlung. Über Bugmeldungen freue ich mich, kann aber nicht versprechen, dass ich sie schnell behebe. Wie erwähnt: die Programme sind aus Neugier entstanden und erfüllen meine Anforderungen. Deswegen habe ich vorerst auch nicht vor, sie auf GitHub oder sonstwo hochzuladen. Wenn du das gerne tun möchtest, tu dir keinen Zwang an. Informiere mich in diesem Fall aber bitte darüber.

Über den Inhalt der Programme und des Scripts verliere ich hier keine großen Worte. Wenn du Interesse hast zu erfahren was hier genau passiert, dann schreib mir das in den Kommentaren. Ich antworte dir dann entweder dort oder schreibe einen neuen Artikel zum Thema.

=-=-=-=-=
Powered by Blogilo

LaTeX – Ein Einstieg

März 13, 2013 um 7:48 vormittags | Veröffentlicht in LaTeX, Ubuntuusers | 7 Kommentare

Lange ist es her, dass ich etwas über LaTeX geschrieben habe. An sich habe ich einen Einstieg in LaTeX schon vor längerer Zeit einmal beschrieben. Dieses Semester leite ich, gemeinsam mit zwei Kollegen an der Uni Wien einen LaTeX-Workshop, in welchem wir versuchen Studienanfänger an LaTeX heranzuführen. Dazu haben wir ein paar Foliensätze zusammengestellt, mit denen wir das jeweilige Thema vorstellen. Die Themen sind folgende:

  • Einstieg/Installation von LaTeX
  • Mathematik-Modus
  • Zitieren mit BibTeX
  • Grafiken verwenden
  • Tabellen bauen
  • Referenzen innerhalb und außerhalb des Dokuments
  • Präsentationen mit Beamer

Wir sind immer noch dabei Foliensätze zu erstellen, deswegen kann ich jetzt nur den ersten Foliensatz online stellen. Die Foliensätze sind so verfasst, dass sie möglichst selbsterklärend sind. Deswegen werde ich hier nicht viel über den Inhalt verlieren. Natürlich besteht LaTeX aus wesentlich mehr als die Folien abdecken können. Es soll eben nur ein Einstieg sein. Weitere Foliensätze folgen, sobald sie in einer finalen Version vorliegen.

Dateien

Einheit01.tex  <– Das haupt-TeX-File
Einheit01.bib  <– Das zugehörige Bib-File (Zitiermaterial)
include.tex  <– Header-File mit allen notwendigen Layout-Informationen und eingebundenen Paketen
cc-by-sa.png  <– CC-Grafik
Einheit01.pdf  <– Fertiges PDF

Anmerkung: Das Original aus dem Workshop hat auf der Titelfolie noch eine zusätzliche Grafik: das Logo des Mentoring-Programms der Uni Wien. Da ich dieses nicht unter eine CC-Lizenz  stellen kann habe ich es kurzerhand herausgenommen. Außerdem habe ich den Header noch nicht aufgeräumt. Hier ist ein Haufen an neuen Kommandos, welche keine logische Struktur haben. All das in Version 2.0 :)

Wie auch im PDF geschrieben: alle diese Dateien stehen unter einer CC-BY-SA 3.0, mit einer Ausnahme: die cc-by-sa.png-Grafik, an welcher alleine Creative Commons die Rechte hält.

=-=-=-=-=
Powered by Blogilo

Playr – Ein iPod Shuffle für die Bash

Januar 21, 2013 um 10:05 vormittags | Veröffentlicht in Bash, Multimedia, Programmieren, Ubuntuusers | 9 Kommentare

Schon länger habe ich nach einer Möglichkeit gesucht ein paar Musikdateien auszuwählen und in zufälliger Reihenfolge abspielen zu lassen. Das kann sowohl ein einzelnes Album sein, als auch eine Zusammenstellung mehrere Tracks aus verschiedenen Verzeichnissen. Außerdem sollte dieses Programm Konsolen-basiert sein, da ich es für schnelle Wiedergabe brauche und nicht zuerst in einer GUI alles zusammenklicken will. Da ich ein solches Programm leider noch nicht gefunden habe, habe ich mir selbst ein Bash-Skript zusammengestellt, welches als Scheduler dient und play (Paket sox aus den Quellen) zum Abspielen verwendet. play kann sowohl OGG, FLAC und WAV abspielen. Nach Installation von libsox-fmt-mp3 auch MP3.

Playr

Playr steht für die Zusammensetzung aus play (dem Programm fürs Abspielen der einzelnen Tracks) und random (engl.: zufällig). Dass es aussieht wie ein Web2.0-Name ist wiederum Zufall (dt.: coincidence).

Die grobe Funktionsweise: Die übergebenen Musiktracks werden in eine versteckte Textdatei geschrieben. Dann wird mit Hilfe von $RANDOM eine Zufallszahl zur Berechnung des nächsten Tracks verwendet. Dieser Track wird sodann abgespielt und aus der Textdatei entfernt. Das verhindert, dass dieser erneut abgespielt wird (was etwas ist, dass ich bei portablen Audio-Playern sehr nervig finde). Solange noch Tracks in der Liste sind wird fortgefahren.

Soll die Wiedergabe abgebrochen werden, muss der Nutzer nur [strg]+[c] drücken. Leider habe ich bis jetzt keine Möglichkeit gefunden zusätzlich dazu auch einfach zum nächsten Track zu wechseln. Man kann natürlich einfach die trap entfernen, dann bricht [strg]+[c] nur das aktuelle play ab. Dann kann jedoch das Skript selbst nicht mehr komfortabel beendet werden.

Das Skript

Wir beginnen mit der trap. Dieses Konstrukt sorgt dafür, dass unser Skript [strg]+[c] verarbeiten kann. Dafür schreiben wir folgende Zeile:
trap quit SIGINT SIGTERM

Diese Zeile führt dazu, dass beim Erhalt der Signale SIGINT (Signal 2) oder SIGTERM (Signal 15) quit ausgeführt wird. Nun ist quit kein Bash-Befehl sondern eine Funktion, die wir uns selbst schreiben, welche das Skript geordnet beendet:
function quit {
    rm -f $TRACK_FILE $TEMP_FILE
    exit 1
}

Diese Funktion schreiben wir vor die Trap. In dieser löschen wir einfach unsere Textdateien, damit sie beim nächsten Programmaufruf nicht stören.

Als nächstes definieren wir zwei Variablen für unsere versteckten Textdateien. Zum einen eine Datei um die Tracklist zu speichern, zum Anderen eine Datei in der die veränderte Liste gespeichert wird:
TRACK_FILE=/tmp/.tracklist.playr
TEMP_FILE=/tmp/.temp.playr

Wir speichern die Dateien in  /tmp , damit sie keine Verzeichnisse zumüllen. Außerdem sollten wir in  /tmp  immer Schreibzugriff haben. Sollte  /tmp  nicht verfügbar sein, kann man das Verzeichnis ja auf  $HOME  ändern.

Nun wirds Zeit sicherheitshalber Überbleibsel einer vorherigen Ausführung zu entfernen:
rm -f $TRACK_FILE $TEMP_FILE

Nur für den Fall, dass etwas total schief gelaufen ist.

Um die Tracklist zu erzeugen schreiben wir nun einfach alle Parameter, die beim Skriptaufruf übergeben wurde in das TRACK_FILE:
for file in " $@ "; do
    echo " $file "
done > $TRACK_FILE

Das funktioniert gewisserweise wie eine foreach-Schleife. Eine beliebige Anzahl Parameter wird durchlaufen ( $@ ). Der jeweils aktuelle Parameter wird in  $file  gespeichert und in der Schleife verarbeitet. Hier wird er einfach an die Standardausgabe geschickt. Die komplette Ausgabe der Schleife wird wieder auf  $TRACK_FILE  umgebogen, wodurch die Ausgabe nicht auf der Kommandozeile erscheint, sondern in der angegebenen Datei.

Die Größe der Tracklist holen wir uns mit wc:
size=$( cat "$TRACK_FILE" | wc -l)

Natürlich könnte man die Datei auch gleich als Parameter für wc angeben:
wc -l " $TRACK_FILE "

Dann jedoch erhält man eine Ausgabe nach folgendem Muster:
6 .tracklist.playr

Man muss also erst noch den eigentlichen Wert extrahieren. Da mache ich lieber den “Umweg” über cat. Ist kürzer.

Nun kommt die eigentliche Hauptschleife des Programmes:
while [[ $size -gt 0 ]]; do
    ## Code
    ((size–))
done

Diese Schleife läuft solange durch, bis  $size  den Wert 0 hat. Da  $size  auf jeden Fall größer als 0 sein muss und in jedem Durchgang um eins verringert wird, ist das irgendwann der Fall. Mit der Abbruchbedingung verhindern wir auch, dass die Schleife ausgeführt wird, wenn die Tracklist leer ist.

Es ist Zeit. Zeit um die Urväter, die Auditoren, die Götter anzurufen. Zeit eine Zufallszahl zu erzeugen. Einer Zufallszahl innerhalb eines Intervalls errechnet man am Besten mithilfe von Modulo. Modulo ist der Name der in der Unterstufe als Restwertdivision bekannten Berechnung.

Kleine Beispiele:  10 / 3 = 3 . Rest:  1 13 / 5 = 2 . Rest:  3 . Uns interessiert immer der Restwert. Dieser ist auf jeden Fall immer  um  1  kleiner als der Divisor (für diejenigen, die schon lange aus der Schule draußen sind :) ).

Bash scheint keine built-in Funktion für Modulo zu haben, dafür gibt es ein paar andere Möglichkeiten eine solche Berechnung durchzuführen. Am ansprechendsten habe ich  let  gefunden:
let "rand = $RANDOM % $size + 1"

In der Variable  $rand  wird das Ergebnis der Berechnung festgehalten.  $size  ist natürlich der Begrenzer.  $RANDOM  ist eine Umgebungsvariable, welche bei jedem Aufruf einen neuen zufälligen Integer (Ganzzahl) ausgibt. Da diese Berechnung bei einer Tracklist-Größe von z.B.  6  den Wertebereich von  0-5  abdeckt, wir aber kein  0 tes Lied, dafür aber ein  6 tes, zählen wir zum Ergebnis einfach  1  dazu.

Den damit errechneten Track erhalten so:
track= " $( head  $TRACK_FILE  -n$rand | tail -n1)

Hier passiert ein bisschen was:
head  -n$rand

filtert die ersten  $rand  Zeilen aus der Liste. Ist  $rand also  2 , dann erhalten wir durch  head   die ersten  2  Zeilen. Dadurch ist der gesuchte Track immer an letzter Stelle in der Liste. Diese letzte Stelle können wir uns nun per  tail  holen:
tail  -n1

Hier holt uns  tail  die  1 ste Zeile von hinten. Damit haben wird unseren Track. Dieser wird nun in die Variable  $track  gespeichert.

Jetzt wird es Zeit den Track abzuspielen:
play " $track "

Natürlich kann man auch das Ausrechnen des Tracks und den Aufruf von  play  in einer Zeile unterbringen. Ich bin jedoch eher für lesbaren Code als für “Zeileneffizienz”.

Zum Abschluss muss noch eine neue Tracklist angelegt werden, ohne den gerade abgespielten Track. Dazu holen wir uns zuerst alle Tracks vor dem Aktuellen:
head " $TRACK_FILE " -n$[ $rand-1 ] > " $TEMP_FILE "

und die Tracks danach:
tail " $TRACK_FILE " -n$[ $size-$rand ] >> " $TEMP_FILE "

Mit
$rand-1

holen wir uns alle Tracks vor dem Aktuellen und sparen diesen aus. Hingegen
$size-$rand

liefert uns alle Tracks nach dem Aktuellen. Damit erhalten wir wieder dieselbe Liste wie zuvor, nur ohne dem letzten Track. Man beachte die Pfeile, welche das Ergebnis jeweils nach $TEMP_FILE schicken. In der ersten Zeile befindet sich nur ein Pfeil. Das bedeutet, dass die Datei überschrieben und neu befüllt wird. Es wird also nur das gespeichert, was in dieser Zeile herauskommt. In der zweiten Zeile hingegen sind zwei Pfeile. Das bedeutet, das das Ergebnis an die Datei angehängt wird. Der Inhalt der Datei wird also nicht über-, sondern der neue Inhalt dahinter in die Datei geschrieben.

Schließlich müssen wir die neue Tracklist noch über die alte schreiben:
mv " $TEMP_FILE " " $TRACK_FILE "

Nach der Schleife selbst rufen wir noch einmal quit auf, damit das Skript sich auch ohne Abbruch durch den Nutzer sauber beendet.

Das vollständige Skript

#!/bin/bash

function quit {
     rm  -f $TRACK_FILE $TEMP_FILE
     exit  1
}

trap  quit SIGINT SIGTERM

TRACK_FILE=/tmp/.tracklist.playr
TEMP_FILE=/tmp/.temp.playr

rm  -f $TRACK_FILE $TEMP_FILE

for  file  in  $@ do
     echo   $file
done > $TRACK_FILE

size=$( cat  “$TRACK_FILE” |  wc  -l)

while [[  $size -gt 0 ]]; do
     let  “rand =  $RANDOM  %  $size  + 1″
    
track= " $( head  $TRACK_FILE  -n$rand  tail  -n1)
     play  $track
     head   $TRACK_FILE  -n$[ $rand-1 ] >  $TEMP_FILE
     tail   $TRACK_FILE  -n$[ $size-$rand ] >>  $TEMP_FILE
     mv   $TEMP_FILE ” “ $TRACK_FILE
    ((size–))
done

quit

Zum Schluss

Ich bin vergleichsweise ein Anfänger in Shell-Script. Ich habe einige Erfahrung in C++ und es kann daher sein, dass ich versuche Konzepte daraus in Bash umzusetzen, obwohl es wesentlich einfachere Lösungen gibt. Wenn du eine bessere Lösung weißt, schreibe sie doch bitte in die Kommentare.

Lizenz

Das Skript steht unter der GPLv3.

Alternativen zu youtube-dl

Dezember 13, 2012 um 9:28 nachmittags | Veröffentlicht in Bash, Free Software/Open Source, Multimedia, Programmieren, Ubuntuusers | 15 Kommentare

Ich mag youtube-dl. Es ist einfach zu benutzen, ist plattformunabhängig (Python) und braucht nicht viele Ressourcen (Terminal-Anwendung).

Leider mag youtube-dl mich zur Zeit nicht. Sobald ich versuche ein Video herunterzuladen schaufelt die Anwendung meinen Arbeitsspeicher voll, sodass alles andere in den Swap ausweichen muss. Das führt dazu, dass das System innerhalb von Sekunden nicht mehr reagiert. Am längsten überlebt noch die Maus, welche sich etwa eine halbe Minute lang stark ruckelnd bewegen kann. Danach ist auch sie tot und das Einzige das hilft ist ein Hard-Reset. Keine wünschenswerte Situation.

Ich habe bereits
youtube-dl -U
als sudo ausgeführt um ein update des Scripts zu erzwingen. Das hat leider nichts geholfen. Der Streaming-Dienst ist auch egal. Ob YouTube, Vimeo oder sonst was, das Problem ist immer gleich.

Logischer Schritt: ich schaue mich nach Alternativen um. Wenn man per
apt-cache search
nach youtube sucht, werden einem bereits eine Liste an Programmen angeboten, welche in der Lage sind Videos von Streaming-Services herunterzuladen. Darunter befinden sich

  • clive
  • cclive
  • fatrat
  • slimrat
  • nicovideo-dl
  • get-flash-videos
  • metacafe-dl
  • nomnom

clive

Speziell clive hat meine Aufmerksamkeit erregt. Es ist ein Terminal-Tool, welches sich für Streaming-Services eignet, die ihre Videos in einem Flash-Player abspielen. Es ist leichtgewichtig, hat eine Vielzahl an praktischen Optionen und verhält sich allgemein sehr wie ich es von youtube-dl gewohnt bin.

Ein einfaches
clive <video_url>
lädt das Video an der angegebenen URL herunter. An sich kann man damit leben. Allerdings hat clive eben mehrere interessante Funktionen, mit denen man sich gerne spielt. Dazu gehört einmal der Schalter -F. Damit erhält man eine Liste von verfügbaren Formaten und Qualitätsstufen in denen das Video vorhanden ist. Diese Liste kommt im Format
hd|mobile|sd
Die durch “|” getrennten Optionen kann man dann in einem zweiten Aufruf dem Schalter -f angeben, welcher dann dafür sorgt, dass clive das Video in der gewünschten Qualität herunterlädt.

Da es etwas unbequem ist für jedes Video zwei Programm-Aufrufe zu starten, habe ich ein einfaches Skript geschrieben, welches diese Aufgabe abnimmt:

#!/bin/bash

VIDEO_URL="$1"
TEMP_FILE=".video-dl_temp_file"

clive -F "$VIDEO_URL" > "$TEMP_FILE"

CHOICES=$(cat "$TEMP_FILE" | tail -n1)

echo -n "Choose format (${CHOICES%% *}|best): "
read FORMAT

clive "$VIDEO_URL" --format="$FORMAT"
--filename-format="%t_$FORMAT.%s"

rm -f "$TEMP_FILE"

Was passiert hier?

VIDEO_URL=”$1
Wir speichern die angegebene URL in einer Variable.

TEMP_FILE=”.video-dl_temp_file
Der Einfachheit halber brauchen wir eine temporäre Textdatei, in welcher wir Informationen speichern können. Der Punkt am Anfang des Dateinamens bedeutet, dass die Datei versteckt ist. Sie wird also im Dateibrowser, oder per ls nicht angezeigt. Die Datei wird am Ende des Skripts wieder gelöscht.

clive -F “$VIDEO_URL” > “$TEMP_FILE
Hier holen wir uns die möglichen Formate, in denen das Video vorliegt und schreiben diese Ausgabe in die temporäre Datei. Da es sich hierbei um zwei Zeilen handelt und nur die zweite Zeile relevant ist, brauche ich:

CHOICES=$(cat “$TEMP_FILE” | tail -n1)
Diese Zeile gibt mir den Inhalt der temporären Datei aus (cat), schneidet die letzte Zeile ab (tail) und speichert die gewünschte Zeile in der Variablen CHOICES.

Als Nächstes fragen wir den Nutzer, welches Format bevorzug wird:
echo -n “Choose format (${CHOICES%% *}|best): 
wobei
${CHOICES%% *}
dafür sorgt, dass hinter der Liste nichts mehr steht. clive gibt hier nochmal die Adresse des Videos aus. Dieser Ausdruck schneidet sie weg.

Dann lesen wir die Wahl des Nutzers ein:
read FORMAT

Nun ist es an der Zeit endlich das Video herunterzuladen:
clive “$VIDEO_URL” –format=”$FORMAT
–filename-format=”%t_$FORMAT.%s

–filename-format gibt die Formatierung des Dateinamens an. %t und %s sind dabei Platzhalter, welche für den Titel und das Suffix der Datei stehen. Zusätzlich zu den Platzhaltern kann man noch beliebig weitere Vorgaben für den Dateinamen machen. Ich füge zwischen Dateinamen und Suffix noch das Format der Datei ein.

Nachdem die Video-Datei sicher auf der Platte liegt lösche ich noch die temporäre Datei:
rm -f “$TEMP_FILE

Fazit

Bis jetzt habe ich clive mit YouTube und Vimeo getestet. Beides funktionierte sehr gut und machte keine Probleme. Das Skript ist natürlich ausbaufähig. Zur Zeit kann es immer nur mit einem Video gleichzeitig umgehen und möglicherweise findet man noch einen besseren Weg als die Informationen in einer temporären Datei zu speichern. Für meine aktuellen Zwecke reichts.

clive steht unter der GPLv3.

=-=-=-=-=
Powered by Blogilo

Bash – Bedingte Ausführung mit if und die while-Schleife

Juli 4, 2012 um 8:12 nachmittags | Veröffentlicht in Bash, GNU/Linux, Programmieren, Ubuntuusers | 8 Kommentare

Wie jede vernünftige Programmiersprache hat auch die Bash eine Möglichkeit Code nur dann abzuarbeiten wenn bestimmte Bedingungen eintreffen. Für alle, die mit Programmierung nicht viel zu tun haben: if wird in den meisten Programmiersprachen dazu verwendet das Programm bestimmte Dinge machen zu lassen, abhängig vom Zustand. Grundsätzlich gibt es für die Auswertung eines Audrucks in wahr und falsch das Kommando test. Allerdings ist if normalerweise ein klein wenig übersichtlicher zu verwenden. Ein Beispiel in C:

if( is_closed(WienerWald) ) {
    gehe_zu(McDonalds);
}
else {
    gehe_zu(WienerWald);
}

Dieses Codestück versucht zu ermitteln ob ein bestimmtes Restaurant geschlossen ist. Wenn es geschlossen ist, wird ein anderes Restaurant besucht. Wenn es nicht geschlossen ist, dann wird das fragliche Restaurant besucht. Die Funktion is_closed() liefert uns entweder wahr oder falsch zurück. Dies sind die einzigen Werte die das if selbst auswerten kann. Auf dieser Grundlage wird dann entschieden welcher Code ausgeführt wird. Wenn der Ausdruck wahr zurückliefert, dann wird der Code gleich nach dem if ausgeführt. Wenn der Ausdruck falsch zurückliefert, dann wird der Code nach dem else ausgeführt. Ist der Ausdruck falsch und es gibt jedoch kein else, wird das ganze if einfach übersprungen. So funktioniert das if in allen (mir bekannten) Programmiersprachen. Aus welchen Datenbeständen das Programm diese Information abfragt ist hier uninteressant. Es soll nur die bedingte Ausführung gezeigt werden.

In Bash ist die Syntax ähnlich:

if [ is_closed $WienerWald ]
then
    gehe_zu $McDonalds
else
    gehe_zu $WienerWald
fi

Wichtig dabei zu beachten ist, dass um die eckigen Klammern Leerzeichen stehen müssen, sonst wird ein Fehler ausgegeben und das if versagt. Ich weiß leider selbst nicht, warum die Bash hier so pickig ist. Bei den Klammern handelt es sich um eine Kurzform des Kommandos test, welches für die eigentliche Auswertung des Ausdrucks zuständig ist.

Vergleich zwischen Zahlen

Zwischen den Klammern kann grundsätzlich alles stehen was irgendwie zu wahr oder falsch abgeleitet werden kann. So können auch Zahlen oder Zeichenketten verglichen werden:

if [ $i -lt 10 ]
then
    echo "0$i"
else
    echo "$i"
fi

Dieses if fragt ab ob die Variable $i kleiner (lt = lesser than) ist als der Wert 10. Ist dies der Fall wird der Zahl eine führende Null vorangestellt, ist dies nicht der Fall, dann nicht. Das kann nützlich sein, wenn man bei formatierten Aufzählungen die Formatierung beibehalten will:

08 - ...             8 - ...
09 - ...    statt    9 - ...
10 - ...             10 - ...

Außer lt gibt es noch folgende Befehle zum Vergleichen von zwei Zahlenwerten:

  • eq = equal            = Gleichheit
  • ne = not equal        = Ungleichheit
  • le = lesser or equal  = kleiner oder gleich
  • ge = greater or equal = größer oder gleich
  • gt = greater than     = größer als

Vergleich zwischen Zeichenketten

Auch Zeichenketten, sogenannte strings, können verglichen werden. Hier gibt es mehrere Möglichkeiten:

if [ $str = "Auto" ]

vergleicht ob die Zeichenkette, welche in der Variable $str gespeichert ist, der Zeichenkette “Auto” entspricht. Wenn dem so ist, dann ist der Ausdruck wahr.

Jetzt wirds ein bisschen kompliziert:

if [ $str != "Auto" ]

vergleicht ebenfalls die Variable $str und die Zeichenkette “Auto“. Dieser Ausdruck ist dann wahr, wenn die beiden Zeichenketten nicht übereinstimmen. Das Rufzeichen agiert hier als Anzeige dafür, dass der gewünschte Vergleich nicht erfolgreich sein soll, damit die Bedingung erfüllt ist.

Als dritte Möglichkeit eine Zeichenkette in einem if zu verwenden kommt die Abfrage ob in der Zeichenkette überhaupt etwas steht:

if [ $str ]

fragt ab ob irgendein Zeichen (egal welches oder wieviele) in der Variable gespeichert sind. Ist mindestens ein Zeichen vorhanden, dann ist der Ausdruck wahr. Ist die Zeichenkette leer (= “”), dann ist der Ausdruck falsch. Zum selben Ergebnis kommt dieser Ausdruck:

if [ -n $str ]

Der Befehl -n prüft ob die Länge der Zeichenkette in der angegebenen Variable nicht Null (non zero) ist. Hingegen

if [ -z $str ]

prüft genau das Gegenteil: -z fragt ob die Länge Null (zero) ist.

UND, ODER und NICHT

Wie jede brauchbare Programmiersprache bietet die Bash bei if auch die Möglichkeit mehrere Ausdrücke hintereinander auszuwerten, ohne jedesmal ein neues if zu benötigen. Der Code

if [ $i -lt 10 ]
then
    if [ $j -lt 10 ]
    then
        ...
    fi
fi

lässt sich kürzer so schreiben:

if [ $i -lt 10 -a $j -lt 10 ]
then
    ...
fi

Das -a (UND) sorgt dafür, dass der Gesamtausdruck nur dann wahr ist, wenn beide Seiten (also links vom -a und rechts davon) jeweils wahr sind. In den meisten Programmiersprachen ist es so, dass zuerst die linke und dann die rechte Seite ausgewertet werden. Sollte die linke Seite bereits falsch sein, wird normalerweise die rechte Seite nicht mehr ausgewertet. Ist im allgemeinen auch nicht notwendig, da der gesamte Ausdruck dann sowieso falsch ist, wenn eine Seite falsch ist. Wahrer als falsch kann es nicht mehr werden. Die Bash hingegen wertet beide Seiten aus. Egal ob der Gesamtausdruck bereits falsch ist oder nicht.

Ähnlich funktioniert das ODER (-o). Hierbei soll geschaut werden ob eine der beiden Seiten wahr ist. Wenn zumindest eine wahr ist, ist der Gesamtausdruck wahr. Auch hier sollte normalerweise der rechte Ausdruck nicht mehr angesehen werden, wenn der Linke bereits wahr ergeben hat. Die Bash machts trotzdem:

if [ $i -lt 10 -o $j -lt 10 ]

Zu guter Letzt im Reich der sogenannten logischen Operatoren gibt es noch das NICHT. Dieses wird als Rufzeichen dargestellt (wie auch schon bei den Vergleichen der Zeichenketten). Seine Aufgabe ist es ein wahr in ein falsch umzukehren und vice versa:

if [ ! $i -lt 10]

Dieser Ausdruck ist dann wahr, wenn $i gerade nicht kleiner ist als 10. Selbstverständlich kann dieses Verhalten auch mit einem anderen Befehl erreicht werden. Im konkreten Fall wäre ein

if [ $i -ge 10 ]

gleichwertig. Das Rufzeichen hat trotzdem seine Existenzberechtigung, weil es manche Ausdrücke wesentlich übersichtlicher werden lässt, da es weniger zu schreiben gibt.

Arbeiten mit Dateien und Ordnern

Es ist mit if in der Bash auch möglich direkt mit Dateien zu interagieren. Ein einfaches

if [ -e datei ]

prüft ob eine Datei mit Namen “datei” existiert. Dabei wird sich immer auch das aktuelle Verzeichnis bezogen. Man kann natürlich auch einen kompletten Pfad angeben, dann ist es egal in welchen Ordner man sich befindet:

if [ -e /Pfad/zur/datei ]

Bisher kann “datei” jedoch sowohl Ordner als auch reguläre Datei sein. Um zwischen diesen Möglichkeiten zu unterscheiden gibt es eigene Befehle:

if [ -f datei ]

prüft ob eine reguläre Datei mit Namen “datei” im aktuellen Verzeichnis liegt. Ist das der Fall, ist das Ergebnis wahr. Es kann natürlich auch ein Ordner mit dem Namen “datei” im aktuellen Verzeichnis liegen. Dann liefert dieser Ausdruck falsch, da ein Ordner mit diesem Namen zwar existiert, aber ein Ordner keine reguläre Datei ist. Hingegen mit

if [ -d ordner ]

wird konkret geprüft ob ein Ordner mit Namen “ordner” an dieser Stelle existiert. Hier gilt dasselbe wie bei Dateien: existiert ein Ordner mit angegebenem Namen, ist der Ausdruck wahr. Existiert kein Ordner mit diesem Namen (egal ob eine Datei mit diesem Namen existiert oder nicht) ist der Ausdruck falsch.

if [ -x executable ]

prüft ob die angegebene Datei ausführbar ist. Das kann ein Programm, ein ausführbares Script oder ein Ordner sein.

Soviel zu den wichtigsten Vergleichen bei der Verwendung von if. Eine vollständige Auflistung gibt es auf der GNU-Infopage zu Bash.

while

Zum Abschluss noch ein Hinweis: das if lässt sich mit drei kleinen Änderungen in eine Schleife verwandeln. Eine Schleife ist ein Gebilde dessen Code mehrmals ausgeführt wird bis eine bestimmte Bedingung eintritt:

while [ $i -lt 10 ]
do
    echo $i
    ((i++))
done

Diese Schleife gibt die Zahlen von n (ein Wert der vorher festgelegt wurde) bis 9 aus. Die '((' und '))' lassen uns ungehindert arithmetische Ausdrücke ausführen. i++ bedeutet, dass der Wert, der in $i gespeichert ist um 1 erhöht wird.

  • Das Wort if muss also durch ein while ersetzt werden
  • then muss durch do ersetzt werden
  • fi muss durch done ersetzt werden

Als Ausdruck in den Klammern ist alles möglich das auch im if möglich ist.

Wichtig: eine while-Schleife verfügt nicht über ein else.

=-=-=-=-=
Powered by Blogilo

Bash – Filename Expansion, for-Schleife

Juli 3, 2012 um 7:52 nachmittags | Veröffentlicht in Bash, GNU/Linux, Programmieren, Ubuntuusers | 14 Kommentare

Die Bash ist ja bekanntlich das Schweizer Taschenmesser beim Umgang mit Unixoiden Systemen. Vor 23 Jahren geboren, heute in der Version 4.2 verfügbar, bleibt sie jedoch immer noch für viele ein Mysterium. Primär für Anfänger ist der Umgang mit ihr umständlich und die große Funktionsvielfalt schier erdrückend. Bis man sich hier durchgekämpft und die steile Lernkurve hinter sich gebracht hat, hat sich bereits eine Haarlänge a’la Rapunzel gebildet. Oder ein Bart nach Stallman. Natürlich zähle ich mich selbst bei weitem nicht zu den Gurus unter den Programmierern. Lerne ich jedoch etwas Praktisches, so versuche ich es leicht verständlich weiter zu geben. Heute geht es um Filename Expansion.

Die GNU-Infopage zu Bash-4.2 lässt sich in Kapitel 3.5.3 etwas umständlich über die genaue Wirkungsweise der Filename Expansion aus. Leider ohne ein Beispiel zu geben. Dieses Versäumnis soll nun nachgeholt werden.

Vorbereitung

Nehmen wir an, wir haben eine Variable $file mit folgendem Inhalt:
/home/user/datei.png

Für bisher-nicht-Bash-Nutzer, so weise ich einer Variable in Bash eine Zeichenkette (ein Pfad zu einer Datei ist eine solche) zu:
file="/home/user/datei.png"
Die Variable $file wird, falls sie noch nicht existiert an dieser Stelle angelegt.

Mit dem Befehl echo kann ich überprüfen, welchen Inhalt die Variable hat:
echo $file
Ausgabe: /home/user/datei.png

Man beachte das Dollar-Zeichen wenn die Variable verwendet wird. Nur mit dem Dollar-Zeichen davor kann auf den Inhalt der Variablen zugegriffen werden.

Der Dateiname

Angenommen ich möchte nun aus unserem Dateipfad den Namen der Datei herauslösen. Hierfür muss ich wohl alles wegschneiden, was vor dem Dateinamen steht:
/home/user/datei.png

Das Konstrukt um dies zu erreichen sieht in Bash so aus:
${file##*/}

Man beachte, dass nach wie vor das Dollar-Zeichen zu Anfang stehen muss. Der Name der Variable ist nun in geschwungenen Klammern eingeschlossen. Die Zeichen dahinter teilen sich in zwei Gruppen auf. Das '##' steht für den Befehl alles von links her wegzuschneiden. Alle weiteren Zeichen, welche darauf folgen, geben an was genau weggeschnitten werden soll.

*/
steht für: alles bis zum Zeichen '/'.

Folgender Befehl:
echo ${file##*/}
ergibt also folgende Ausgabe:
datei.png

Nun kann man argumentieren, dass in der Zeichenkette
/home/user/datei.png
ja viele '/' vorkommen. Das stimmt. Das '##' schneidet eben alles bis zum letzten '/' weg.

Ähnlich funktioniert das Konstrukt
${file#*/}
Man beachte das einfache '#'. Dieser Befehl schneidet alles bis zum ersten '/' weg.

Folgender Befehl:
echo ${file#*/}
ergibt also folgende Ausgabe:
home/user/datei.png
Man beachte das fehlende '/' am Anfang.

Das Suffix

Dieses Spielchen funktioniert natürlich auch von der rechten Seite her. Statt dem '##', bzw. dem '#', verwenden wir hier ein '%%', bzw. ein '%'.

Nehmen wir an, wir haben den kompletten Dateinamen (inklusive Endung) in einer weiteren Variablen gespeichert:
file_name=${file##*/}

Ein
echo $file_name
bringt uns nun folgende Ausgabe:
datei.png

Wir wollen nun nur den eigentlichen Dateinamen herausholen, ohne Endung. Dazu müssen wir die Endung wegschneiden:
datei.png

Das funktioniert so:
${file_name%.*}

Das '%' funktioniert hier als Anweisung die Zeichenkette von rechts her zu beschneiden. Weggeschnitten werden soll '.*', also alles was rechts vom ersten '.' steht.
(Natürlich macht hier die Unterscheidung zwischen erstem und letztem Auftreten eines Zeichens keinen Unterschied, da das Zeichen hier nur einmal vorkommt.)

Anwenden

Manch einer fragt sich sicherlich schon: und wofür brauche ich das? Nun, die Bash ist zwar sehr komplex, wenn man sie jedoch mal ein wenig verstanden hat auch sehr flexibel und effizient. Ich erledige viele Aufgaben, für welche ich mit grafischen Programmen viele Klicks und mehrere Minuten brauche (z.B. Bildbearbeitung mit Gimp), in wenigen Sekunden.

Nehmen wir an, wir wollen von unserem Bild ein Thumbnail für eine Website erzeugen. Das Thumbnail soll etwa ein Fünftel der Größe des Originals haben (also 20%). Hierzu verwenden wir das Programm convert, welches zum Paket imagemagick gehört. Der Befehl sieht dann so aus:
file="datei.png"
convert "$file" -resize 20% "${file%.*}_thumb.png"

Damit wird eine neue Datei erzeugt, welche 20% der Größe der Datei datei.png hat und datei_thumb.png heißt. Hier haben wir die Filename Expansion also verwendet um den Dateinamen anzupassen, den wir der neuen Datei gegeben haben.

Auf mehrere Dateien anwenden

Das ist selbstverständlich noch nicht der Weisheit letzter Schluss. Für eine einzelne Datei zeigt sich die Flexibilität von Filename Expansion noch nicht so wirklich. Erst wenn ich mehrere Dateien, mit stark unterschiedlichen Namen bearbeiten will sieht man das Potential.

Nehmen wir also an, wir haben einen Ordner voller Bilder:
Geburtstag1.png
Valerie_Donau.jpg
Visitenkarte.png
Zweiwohnschloss.gif
...

Von allen diesen Bildern möchten wir, wie im obigen Beispiel, ein Thumbnail erzeugen lassen. Wir brauchen nun alle Techniken, die wir oben gelernt haben, um dieses Vorhaben elegant umzusetzen. Zu aller erst brauchen wir eine Schleife. Eine Schleife ist ein Konstrukt, welches den Code, der in der Schleife steht, sooft ausführt, bis eine bestimmte Bedingung erreicht ist. Unsere Schleife sieht so aus:

for file in ./*; do ; done

Diese Schleife macht noch nichts. Hier will ich nur den grundsätzlichen Aufbau erklären. for ist das Schlüsselwort mit dem die Schleife beginnt. Danach wählen wir einen Namen für eine Variable, die wir in der Schleife verwenden wollen (hier: file). in ist wieder ein Schlüsselwort, welches wir bei der Schleife brauchen. Danach steht was in die vorher gewählte Variable eingetragen werden soll. ./* steht für “alle Dateien in diesem Ordner”. Wir gehen davon aus, dass in diesem Ordner nur Bild-Dateien enthalten sind.

Da wir unseren Code nicht in ein Shell-file schreiben, sondern direkt auf die Kommandozeile sind die Semikolons zwingend. Nach jedem Befehl muss ein Semikolon folgen.

do leitet den Code der Schleife ein. Der erste Befehl der Schleife folgt gleich auf das do, ohne Semikolon. Mit done wird die Schleife abgeschlossen. do und done sind keine Befehle, deshalb muss auf sie nicht direkt ein Semikolon folgen.

Die Bedingung dieser Schleife ist: gehe alle Dateien in diesem Ordner durch. Für jede Datei in diesem Ordner wird die Schleife also genau einmal ausgeführt. Der jeweilige Name der Datei findet sich in der Variable $file.

Nun schreiben wir den Code unserer Schleife. Zuerst müssen wir den eigentlichen Namen (das Prefix) der jeweiligen Datei finden:

for file in ./*; do prefix="${file%.*}"; done

Danach brauchen wir noch das Suffix (Endung; denn in unserem Ordner liegen ja Bild-Dateien von verschiedenen Typen herum herum):

for file in ./*; do prefix="${file%.*}"; suffix="${file##*.}"; done

Nun können wir den Konvertierungsbefehl aus dem obigen Beispiel einfügen (mit einer kleinen Änderung):

for file in ./*; do prefix="${file%.*}"; suffix="${file##*.}"; convert "$file" -resize 20% "$prefix"_"thumb.$suffix"; done

Für unsere gegebenen Dateien sieht das Ergebnis so aus:

Geburtstag1.png
Geburtstag1_thumb.png
Valerie_Donau.jpg
Valerie_Donau_thumb.jpg
Visitenkarte.png
Visitenkarte_thumb.png
Zweiwohnschloss.gif
Zweiwohnschloss_thumb.gif
...

Diese Schleife hat bei mir für 70 Bilder zu jeweils ca. 5 MB etwa 1:30 min gebraucht. Schaffst du das mit dem Gimp auch? :)

=-=-=-=-=
Powered by Blogilo

ein kleines LaTeX-HowTo – Alles für “Theoretische Informatik” – und ein Exkurs

März 29, 2012 um 10:05 nachmittags | Veröffentlicht in LaTeX, Programmieren, Ubuntuusers | 3 Kommentare

Nach langer Zeit wollte ich mal wieder ein LaTeX-HowTo schreiben in welchem ich meine eigenen Erfahrungen mit LaTeX niederschreibe. Ursprünglich sollte das Thema, passend zu meinem Artikel Zitate und Literaturverzeichnis mit BibTex zu tun haben. Da das Thema aber recht umfangreich ist wollte ich hierfür auch eine Menge an Anschauungsmaterial liefern um die Unterschiede unter verschiedenen Styles hervorzuheben. Dieser Artikel muss leider noch warten. Stattdessen möchte ich hier kleine Hilfestellungen die mir meine Hausaufgaben in der Lehrveranstaltung “Theoretische Informatik” verschönt haben zeigen.

Kleiner Exkurs: Bei der Theoretischen Informatik handelt es sich um das Wissen wie grundlegende Mechanismen funktionieren. Dazu gehört z.B. die Automatentheorie. Kurz gesagt: nach dieser LV hat man den Großteil des theoretischen Hintergrundes intus mit welchem man einen Compiler bauen könnte. Das heißt aber natürlich nicht, dass man sich danach gleich daran setzt eine neue Programmiersprache zu schreiben.

Wie du siehst verknüpfe ich die von mir verwendeten Befehle und Hilfsmittel mit einem einfachen konkreten Beispiel welches direkt aus der Lehrveranstaltung kommt. Wenn dich die Theorie nicht interessiert kannst du auch einfach nur den Code und den gerenderten Text analysieren. Da dieser Artikel zum Teil auch für mich als Übung und als Erinnerung herhalten soll gehe ich auch auf die theoretische Vorgangsweise näher ein.

Der LaTeX-Code ist als Bilder eingefügt weil Blogilo permanent die Backslash frisst. Mit diesen Backslash beginnt in LaTeX jedoch jeder Befehl und eine Reihe von Maskierungen.

Praktische Befehle

Die Kapitel sind nach dem Anwendungsgebiet benannt.

Formale Beschreibung

Ein Beispiel beginnt mit der formalen Beschreibung einer gegeben Sprache. Die Sprache ist dann z.B. als regulärer Ausdruck gegeben:

Beschreiben Sie die durch den regulären Ausdruck (y | x)+ definierte formale Sprache mittels Mengenschreibweise.

Erklärung der Angabe: Diese Angabe besagt, dass entweder y oder x beliebig oft (0 bis unendlich) vorkommen dürfen (dafür steht der jeweilige Stern). Durch das Plus am Ende wird angegeben, dass der ganze Ausdruck in der Klammer mindestens einmal aber ansonsten beliebig oft (eben 1 bis unendlich) vorkommen soll. Dadurch kann jede beliebige Zeichenkette erzeugt werden welche aus den Zeichen x und y besteht, in beliebiger Reihenfolge und beliebig lange. Obwohl das Plus darauf besteht, dass der Ausdruck mindestens einmal vorkommen muss kann das Ergebnis auch leer sein, da x und y ja auch 0-mal vorkommen können.

Für die Mengenschreibweise empfiehlt sich dann der Mathe-Modus von LaTeX, welcher mit dem $-Zeichen beginnt und endet. Die Lösung als LaTeX-Code und als gerenderter Text:

Code 01

Hierbei stehen folgende Zeichen für das jeweilige Ergebnis:

  • Code 02 steht für eine geöffnete geschwungene Klammer die auch angezeigt werden soll. Der Backslash ist in diesem Fall wichtig, weil er die Klammer maskiert. Ohne ihm sind wir beim nächsten Punkt:
  • Code 03 das ^ (caret) ist das Signal an LaTeX das nachfolgende Zeichen hochzustellen. Mit den Klammern wird eine ganze Zeichenkette eingeschlossen auf die dieser Befehl angewandt werden soll. Diese wird dann, wie im Beispiel oben, als Hochzahl dargestellt. Es können natürlich auch mehr als ein Zeichen sein. Weder das caret noch die Klammern werden im Ergebnis sichtbar sein. Sie dienen nur als Formatierungs-Befehle.
  • Code 04 wird zur sogenannten Pipe. Das ist der senkrechte Strich in der Mitte der Definition. Hinter diesem Befehl muss ein Leerzeichen folgen, da der Befehl sonst nicht erkannt wird. Dieses Leerzeichen wird nicht ausgegeben.
  • Code 05 steht für “greater or equal” und produziert das größergleich-Zeichen. Das Gegenstück heißt “lesser or equal”: leq.

Grammatik

Nachdem wir diese Definition geschafft haben ist der nächste Schritt eine reguläre Grammatik zu erzeugen mit der wir alle Wörter aus dem regulären Ausdruck erzeugen können:

Konstruieren Sie die entsprechende reguläre Grammatik, die die Sprache aus Beispiel 1 erzeugt, und spezifizieren Sie einen Automaten, der sie akzeptiert.

Unsere Grammatik sieht so aus:

Code 06

Example 02

Da in diesem Code nichts Neues vorkommt nehmen wir ihn einfach als gegeben hin. Das erste S ist die Liste aller Symbole die wir für die Ersetzungsvorgänge benötigen (in diesem Fall eben nur das S). x und y sind die Symbole welche wir zum eigentlichen Schreiben eines Wortes zur Verfügung haben. P ist die Menge aller Regeln. Hierfür werden wir eine eigene Zeile benötigen. Das letzte S ist im Grunde genommen dasselbe S wie das S am Anfang, nur wird es hier als Startsymbol verwendet. Dabei handelt es sich um eine Konvention. Irgendwo müssen wir ja anfangen.

Regelwerk

Kommen wir zur Menge der Regeln. Hier gibt es wieder eine interessante Neuerung:

Code 07

Der Befehl overset ist hier der wichtigste von allen. Lange habe ich nach einer Möglichkeit gesucht ein beliebiges Zeichen über ein anderes stellen zu können. Dieser Befehl bewirkt genau das. Er übernimmt zwei Parameter und stellt das Ergebnis des Ersten über den Zweiten. In diesem Beispiel besteht P aus 3 Regeln. Die Syntax einer Regel besteht aus:

Ausgangssymbol -> Ergebnis

wobei jeder Pfeil eine fortlaufende Nummer hat. Ich hätte natürlich auch schreiben können: ->1 . Aber ehrlich: Das wäre das Ergebnis mit MS Word. LaTeX kann es besser. Der Befehl rightarrow zeichnet dann den einfachen Pfeil nach rechts. Das kleine e-ähnliche Zeichen ist das kleine griechische Epsilon und wird als “Leerwort” verwendet. Sprich: damit verschwindet die Variable und ich bin fertig. Der Befehl für das kleine Epsilon: epsilon und für das große Epsilon: Epsilon. Das funktioniert auf diese Art und Weise mit allen griechischen Zeichen.

Der Automat

Nun ist in der Angabe noch ein Automat gefordert, welcher alle Wörter annehmen kann, welche von der Grammatik, die wir gerade erstellt haben, erzeugt werden können. Die Definition des Automaten:

Code 08

Example 04

beinhält nur zwei Punkte die noch nicht angesprochen wurden. Analog zum caret (^) stellt der Unterstrich im Mathe-Modus das nachfolgende Zeichen herab. Ist danach eine Zeichenkette in geschwungenen Klammern angegeben wird die ganze Zeichenkette herabgestellt. Der Befehl delta wiederum ist analog zum Befehl epsilon und erzeugt ein kleines griechisches delta-Zeichen.

Der erste Punkt in der Definition ist die Menge der möglichen Zustände des Automaten. In diesem Fall ist das nur q0. Danach kommen die möglichen Zeichen die der Automat akzeptieren muss (hier: x und y). Danach kommt der Name der Tabelle in welcher die Verbindungen des Automaten definiert werden. Diese wird mit dem delta beschriftet. Danach kommt der Startzustand (wie das S bei den Grammatik-Regeln). Dabei handelt es sich wieder um q0. Zum Schluss geben wir die Menge an möglichen Endzuständen des Automaten an. Diese Ehre kommt wieder q0 als einzigem Zustand des Automaten zu.

Die Tabelle

Nun wollen wir die Tabelle des Automaten erzeugen:

Code 09

Example 05

Da es sich um eine tabellarische Auflistung handelt liegt es nahe eine einfache Tabelle zu erzeugen. Dazu müssen wir mit begin eine neue Umgebung eröffnen. Diese Umgebung soll vom Typ tabular sein. Nicht vergessen die Umgebung mit end wieder zu schließen. Als Argument geben wir der Tabelle noch Folgendes mit: l|ccDie lesbaren Zeichen stehen für die Ausrichtung des Textes in der jeweiligen Spalte. Die erste Spalte soll also linksbündig sein, die beiden anderen centriert. Mit der Pipe zwischen der ersten und zweiten Spalte gebe ich an, das LaTeX einen senkrechten Strich zwischen diesen beiden Spalten ziehen soll.

Die Tabelle beginnt dann mit einfachem Text. In diesem Fall ist es das Zeichen delta, welches nur im Mathe-Modus dargestellt werden kann. Die einzelnen Spalten werden durch das Kaufmännische Und (&) getrennt. Nach der letzten Spalte muss ein Zeilenumbruch erfolgen. Diesen erzwingt man mit den beiden Backslash am Ende der Zeile. Die Zeichen x und y müssen nicht unbedingt im Mathe-Modus geschrieben werden. Ich habe es dennoch gemacht um das Schriftbild zu vereinheitlichen. Der Befehl hline erzeugt mir den waagrechten Strich den ich benutze um die Kopfzeile vom Körper der Tabelle zu trennen. Dieser Befehl sollte für sich in einer Zeile stehen und braucht nicht mit einem Zeilenumbruch bedacht zu werden. Das macht er selbst. Auch die letzte Zeile in der Tabelle muss nicht mit einem Zeilenumbruch beendet werden. Das passiert beim beenden der Tabelle mit end.

Zur Erklärung der Tabelle: Das delta ist der Bezeichner (Name) der Tabelle. Daneben stehen die Zeichen die der Automat verarbeitet in relativ beliebiger Reihenfolge. In der linken Spalte sind alle Zustände aufgelistet. Dem Automaten werden nun die einzelnen Zeichen des Eingabewortes gefüttert. Je nachdem in welchem Zustand man sich gerade befindet muss man nur in die Spalte mit dem jeweiligen Zeichen sehen um zu erkennen in welchen Zustand der Automat nun wechselt. Ist das Wort zu Ende und der Automat ist in einem akzeptierten Endzustand stehengeblieben (die Menge welche wir bei der Definition angegeben haben) gilt das Wort als akzeptiert. Wenn nicht, dann nicht.

Dieser Automat funktioniert wie folgt: Wir starten im Zustand q0 und bekommen einer der beiden Zeichen x oder y. In beiden Fällen wechseln wir vom Zustand q0 in den Zustand q0. Das bedeutet, wir akzeptieren in jedem Durchgang jedes Zeichen. Da q0 auch gleichzeitig ein Endzustand ist können wir ohne Probleme jedes beliebig lange Wort akzeptieren, da wir immer in q0 zu stehen kommen werden.

Soweit zu den Techniken die ich bisher in der LV Theoretische Informatik verwendet habe. Ein großes Kapitel habe ich noch nicht angeschnitten: LaTeXDraw. Dieses werde ich in Kürze in einem eigenen Artikel behandeln. Auch dieser Artikel wird ein konkretes Beispiel aus der LV behandeln und Schritt für Schritt vorgehen.

Ich hoffe du konntest etwas nützliches über LaTeX lernen.

=-=-=-=-=
Powered by Blogilo

Bash – rm und der Standard input

Mai 26, 2011 um 12:58 vormittags | Veröffentlicht in Bash, GNU/Linux, Ubuntuusers | 15 Kommentare

Mit einem bestimmten Problem habe ich mich in letzter Zeit herumgeärgert: Ich möchte einen Haufen Dateien deren Namen über den Standardeingabekanal der Bash hereingeflogen kommen mit einem Befehl löschen. Natürlich könnte ich jede einzelne Datei angeben. Diese Methode wird aber sehr aufwändig wenn ich alle Dateien löschen möchte, deren Namen nicht in der Liste auftauchen.

Achtung, dank mehrerer Kommentare habe ich gelesen, dass ls hier die falsche Wahl ist. Eine bessere Alternative scheint find zu sein.

Anwendungsbeispiel

Ich habe mehrere ähnliche Dateien in meinem Directory. Diese werden automatisiert bearbeitet und das Ergebnis jeweils in einer Datei mit dem Zusatz Clean gespeichert. Nun interessieren mich die ursprünglichen Dateien nicht mehr und ich möchte sie ebenso automatisiert löschen. (Nein, ein Ersetzen der Urprungsdateien gleich beim Bearbeiten fällt leider aus technischen Gründen aus.) Ein kurzes ls gibt uns z.B. folgende Liste aus:

taach@prompt:~/test$ ls
Datei 1 Clean.txt
Datei 1.txt
Datei 2 Clean.txt
Datei 2.txt
Datei 3 Clean.txt
Datei 3.txt
Datei 4 Clean.txt
Datei 4.txt
Datei 5 Clean.txt
Datei 5.txt

Zum Glück stellt ls mit --ignore bereits eine Möglichkeit Dateinamen zu ignorieren die bestimmte Muster enthalten:

taach@prompt:~/test$ ls –ignore=*Clean.txt
Datei 1.txt
Datei 2.txt
Datei 3.txt
Datei 4.txt
Datei 5.txt

Diese Variante ist jedoch Case sensitive. Wenn das Muster also in verschiedener Groß-/Kleinschreibung vorliegt, empfiehlt es sich stattdessen grep einzusetzen:

taach@prompt:~/test$ ls | grep -iv Clean
Datei 1.txt
Datei 2.txt
Datei 3.txt
Datei 4.txt
Datei 5.txt

Die Option -i von grep schaltet die Case sensitivity aus und -v negiert die Auswahl. In diesem Fall werden also alle Zeilen entfernt in denen das Muster Clean oder clean vorkommt. An sich könnten wir nun diese Liste rm übergeben. Dafür verwenden wir xargs, welches notwendig ist, dass rm die Liste aus der Standardeingabe auch wirklich übergeben wird. Wenn die Dateinamen jedoch, wie in diesem Fall, Leerzeichen enthalten bringt uns rm diese Fehlermeldung:

taach@prompt:~/test$ ls –ignore=*Clean.txt | xargs rm
rm: Entfernen von „Datei“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „01.txt“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „Datei“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „02.txt“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „Datei“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „03.txt“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „Datei“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „04.txt“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „Datei“ nicht möglich: Datei oder Verzeichnis nicht gefunden
rm: Entfernen von „05.txt“ nicht möglich: Datei oder Verzeichnis nicht gefunden

Das passiert deswegen, weil die Leerzeichen ungeschützt vorliegen und die Bash, bevor die Liste nun endgültig rm übergeben wird, diese als Ende eines Dateinamens interpretiert. Wir müssen also xargs sagen, dass die Leerzeichen maskiert bleiben müssen ('\ ' statt ' '), damit die Bash sie ignoriert. Dazu gibt es die Option -0 welche jegliche Maskierungen aufrecht erhält:

taach@prompt:~/test$ ls –ignore=*Clean.txt | xargs -0 rm
rm: Entfernen von „Datei 01.txt\nDatei 02.txt\nDatei 03.txt\nDatei 04.txt\nDatei 05.txt\n“ nicht möglich: Datei oder Verzeichnis nicht gefunden

Verdammt, was ist denn jetzt schon wieder los? Ah, zwischen den Dateinamen ist plötzlich ein Zeichen aufgetaucht: '\n' Dieses Zeichen, auch Endline genannt, kennzeichnet einen Zeilenumbruch. Da wir xargs gesagt haben, dass alle Maskierungen aufrecht erhalten bleiben sollen, liegt nun auch der Zeilenumbruch als Reintext vor. Und da das Zeichen nun einfacher Text ist wird die ganze lange Wurst wie ein einziger Dateiname behandelt. Nun müssen wir xargs also noch mitteilen, wo die Dateinamen enden. Dazu verwenden die Option -d mit dem Parameter '\n':

taach@prompt:~/test$ ls –ignore=*Clean.txt | xargs -0 -d ‘\n’ rm

und siehe da alle Dateien, mit Ausnahme derer mit Clean im Namen wurde gelöscht:

taach@prompt:~/test$ ls
Datei 1 Clean.txt
Datei 2 Clean.txt
Datei 3 Clean.txt
Datei 4 Clean.txt
Datei 5 Clean.txt

Das ganze geht natürlich auch so:

taach@prompt:~/test$ rm `ls –ignore=*Clean.txt | xargs -0 -d ‘\n’`

bzw. im Bash-Style:

taach@prompt:~/test$ rm $(ls –ignore=*Clean.txt | xargs -0 -d ‘\n’)

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

=-=-=-=-=
Powered by Blogilo

Nächste Seite »

Bloggen Sie auf WordPress.com. | The Pool Theme.
Einträge und Kommentare feeds.

Follow

Erhalte jeden neuen Beitrag in deinen Posteingang.