Skriptsprachen sind wie Baukästen mit einer Vielzahl kleiner Teile, aus denen sich große Maschinen bauen lassen. Welche Rädchen es gibt und wie Sie sie in Bewegung setzen, erfahren Sie hier.
Früher war die Unix-Welt noch einfach: Für “richtige” Programme verwendete man C, für kleine selbst geschriebene Skripts die Shell-Sprache in Kombination mit Tools wie Sed und Awk. Heute stehen für den programmierwilligen Linux-Anwender dutzende so genannter Skriptsprachen zur Auswahl. Dieser Artikel vergleicht die derzeit beliebtesten Skripting-Tools Perl, Python und Ruby und gibt eine kurze Einführung in die Programmierung.
Skripten oder kompilieren
Obwohl die Grenzen zu kompilierten Sprachen wie C immer mehr verschwimmen (zum Beispiel mit Mono), zeichnen sich Skriptsprachen dadurch aus, dass der Programmierer den geschriebenen Code nicht in einem separaten Vorgang übersetzen muss. Stattdessen tippt er sein Programm ein und kann es sofort ausführen – vorausgesetzt es enthält keine groben Fehler. Das Ausführen übernimmt der so genannte Interpreter, ein Kommandozeilenprogramm, das bei den hier vorgestellten Beispielen so heißt wie die Sprache selbst: perl, python und ruby.
TIPP
Um ein Skript direkt durch Aufruf seines Namens (etwa mit skript statt mitpython skript) auszuführen, geben Sie in der ersten Zeile des Skripts den Aufruf für den Interpreter an – für Python etwa #!/usr/bin/python. Zusätzlich machen Sie die Skriptdatei ausführbar, indem Sie auf der Kommandzeile chmod a+x skript eingeben.
Beim Funktionsumfang können Skriptsprachen längst mit den kompilierten Kollegen mithalten. War die Domäne von Skriptsprachen wie der Shell früher vor allem die Systemadministration mit relativ kleinen Tools für eine Aufgabe, werden heute komplette Desktop-Anwendungen in Skriptsprachen entwickelt – so der Gnome-Network-Manager in Python. Diese Skriptsprache hat sich in den letzten Jahren eine große Fan-Gemeinde aufbauen können und gilt beispielsweise bei Ubuntu Linux neben dem herkömmlichen C als wichtigste Sprache – auch weil Ubuntu-Förderer Shuttleworth selbst gerne damit programmiert.
Prinzipiell lassen sich mit den Skriptsprachen Perl, Python und Ruby praktisch identisch funktionierende Programme schreiben. Auch laufen solche Skripts oder Anwendungen mehr oder weniger gleich schnell. Kommt es auf die Ausführungsgeschwindigkeit an, etwa bei komplizierten Berechnungen, bringt kluge Programmierung mehr als die Wahl der “besten” Sprache.
Tatsächlich unterscheiden sich diese drei Skriptsprachen für den Einsteiger nur wenig voneinander, auch wenn ihre jeweiligen Fans das anders sehen mögen. Der Hauptunterschied liegt in der so genannten Syntax: den Regeln dafür, wie ein bestimmter Code-Ausdruck in einer Programmiersprache “wohlgeformt” oder korrekt ist. Die Korrektheit der Syntax prüft der Interpreter als erstes, sobald der Benutzer das Skript ausführt. So erwartet zum Beispiel Perl hinter jeder Anweisung ein Semikolon, die anderen beiden Sprachen nicht.
Ein kurzes Beispiel soll den Unterschied illustrieren und einen kleinen Vorgeschmack auf die einzelnen Skriptsprachen geben: Das Beispielprogramm berechnet eine Summe aus zwei Zahlen und gibt das Ergebnis mit einem erläuternden Text aus. Die entsprechenden Skripts finden Sie in den Listings 1 bis 3.
Listing 1
Perl
$a = 3; $b = 5; $c = $a + $b; print "Die Summe ist ", $c;
Listing 2
Python
a = 3 b = 5 c = a + b print "Die Summe ist", c
Listing 3
Ruby
a = 3 b = 5 c = a + b print "Die Summe ist ", c
Sehr groß fallen die Unterschiede nicht aus. Allerdings handelt es sich ja nur um ein sehr einfaches Programm. Neben den erwähnten Semikolons stechen in Listing 1 die Dollarzeichen ins Auge, mit denen Perl die Variablen kennzeichnet. Auch der Print-Befehl verhält sich etwas anders. In Python hängt er automatisch einen Zeilenumbruch an und schiebt durch das Komma ein Leerzeichen ein. Bei Perl und Ruby muss der Programmierer das Leerzeichen selbst hinschreiben und gegebenenfalls mit dem Zeichen \n einen Zeilenumbruch erzeugen. Die letzte Zeile sähe dann so aus: print "Die Summe ist ", c, "\n".
Die drei Skriptsprachen erlauben außerdem, Variablen auch innerhalb von Zeichenketten (Strings) auszugeben, zum Beispiel mit Perl print "Die Summe ist $c\n" oder in Ruby print "Die Summe ist #{c} \n". Der Python-Programmierer muss dazu erst einen Platzhalter innerhalb des Strings setzen, gefolgt von der Variablen selbst hinter einem Prozentzeichen: print "Die Summe ist %d" % c.
Funktional ist das Programm in allen drei Sprachen identisch: Die Variablen speichern wie in der Schulalgebra Zahlen und dienen dann zur Berechnung, deren Ergebnis in einer dritten Variablen gespeichert wird. Anders als in der Mathematik können Variablen alle möglichen Inhalte speichern – nicht nur Zahlen, sondern auch Zeichen, so genannte Zeichenketten (Strings), ganze Dateien und vieles mehr.
Strings und andere Typen
Programmierer nennen die Variablenarten auch Datentypen. Einige Typen sind in Programmiersprachen schon eingebaut, zum Beispiel Ganzzahlen (Integer), Fließkommazahlen (Real oder Float), Einzelzeichen (Characters/Chars) sowie die erwähnten Zeichenketten (Strings). Die kompilierte Sprache C besitzt beispielsweise im Gegensatz zu den Skriptsprachen keinen eigenen Typ für Zeichenketten, weshalb die String-Verarbeitung mit Skriptsprachen wesentlich leichter fällt. So kann der Skript-Programmierer einer Variablen einen String zuweisen und damit arbeiten wie mit einer Zahlvariable. So lassen sich mit dem Pluszeichen zum Beispiel zwei Strings aneinanderfügen.
Wesentlich zur Einfachheit von Perl, Python und Ruby trägt bei, dass es sich um so genannte dynamisch typisierte Sprachen kandelt. Das bedeutet, dass eine Variable, die irgendwann einmal eine Zahl gespeichert hat, im weiteren Programmverlauf durchaus auch einmal einen String aufnehmen kann. Beim Gegenteil, den statisch typisierten Sprachen, bleibt eine Variable ihr ganzes Leben lang vom gleichen Typ.
Dynamisch typisierte Sprachen machen vieles leichter. Der Programmierer muss sich erst einmal gar keine Gedanken über Typen machen, sondern kann einfach eine Variable einführen und sie verwenden, wofür auch immer er will. Der Skriptsprachen-Interpreter führt hinter den Kulissen Protokoll darüber, welchen Typ die Variable zu jedem Zeitpunkt gerade besitzt. In den Listings 1 bis 3 sind die Variablen a, b und c immer vom Typ Integer (Ganzzahl). Ein einfaches Beispiel in Python (Listing 4) zeigt, wie der Interpreter den Datentyp je nach Programmablauf umwandelt, ohne dass der Programmierer dafür etwas tun muss.
Listing 4
Python
a = 3 b = 5.0 c = 0 print "c =", c c = a + b print "Die Summe ist", c
Speichern Sie das Listing in der Datei typen.py und führen Sie es mit python typen.py aus. Sie sehen dann, dass c beim ersten Aufruf von print keinen Dezimalpunkt führt. Zu diesem Zeitpunkt besitzt es den Wert 0, ist also eine Ganzzahl. Nach der folgenden Addition sieht es anders aus: Da es sich bei einem der Summanden (b) um eine Fließkommazahl handelt, führt Python eine Fließkomma-Addition aus, deren Ergebnis wiederum eine Fließkommazahl ist. Von den syntaktischen Details abgesehen, funktionieren Perl und Ruby genauso.
Komplexe Typen
Nur mit Zahlen und Strings kommt man beim Programmieren in den seltensten Fällen aus. Oft ist es günstig, Variablen von Einzelwerten (Skalarvariablen) zu einer Gruppe zusammenzufassen, die als solche einfacher zu verarbeiten ist. Es wäre prinzipiell möglich, in einer Adressverwaltung für jeden Namen eine eigene Variable zu verwenden, zum Beispiel a="Maier", b="Huber" und so fort. Der Programmierer müsste im weitere Verlauf dann immer im Auge behalten, wieviele und welche Adressvariablen es gibt – eine äußerst undankbare Aufgabe. Die Skriptsprachen bietet zur Lösung dieses Problems zwei komplexe Datentypen: Listen und Hashes.
Listen, auch Arrays genannt, indizieren die gespeicherten Daten über Integer-Zahlen ab 0, sodass sich das erste Element über liste[0] ansprechen lässt, das zweite über liste[1] und so fort. Die Liste als ganzes kann man stets einfach über den Variablennamen liste erreichen. Listen speichern üblicherweise alle verfügbaren Datentypen einer Sprache – also Integers, Strings, und so weiter, ja sogar wieder Listen.
Im Falle der Adressdaten könnte zum Beispiel eine Liste die Namen speichern: namen[0]="Maier", namen[1]="Huber". Um einer Liste gleich mehrere Werte zuzuweisen, verstehen die Skriptsprachen Konstrukte der Art liste = ["Maier", "Huber"]. Perl weist bei der Referenzierung von Array-Variablen eine Besonderheit auf: Um auf einzelne Elemente zuzugreifen, schreibt man auch Array-Variablen mit einem Dollarzeichen ($namen[0]), auf die komplette Liste bezieht man sich mit einem Klammeraffen (@namen).
Hashes (in Python “Dictionaries”) ähneln Arrays insofern, als sie ebenfalls listenförmig aufgebaut sind. Allerdings dienen bei Hashes keine Zahlen als Indizes, sondern Strings als so genannte Keys (Schlüssel). Diese Art der Datenspeicherung in Variablen erweist sich für viel Anwendungen als praktisch. Sie ermöglicht beispielsweise, die Telefonnummern der Adresssammlung so zu speichern, dass sie sich über eine einfachen Variablenreferenzierung erreichen lassen: telefon['Huber']=123456. Ähnlich wie bei den Arrays hält Perl auch bei Hashes wieder eine Besonderheit bereit. Hashes als Ganzes referenziert man über ein voran gestelltes Prozentzeichen: %telefon. Die Listings 5 bis 7 geben Beispiele für die Verwendung von Listen und Hashes in Perl, Python und Ruby, welche die syntaktische Unterschiede illustrieren.
Listing 5
Perl
@namen = ('Huber', 'Maier');
print $namen[1];
%telefon = ('Huber' => 12345);
print $telefon{'Huber'};
$telefon{'Maier'} = 54321;
Listing 6
Python
namen = ['Huber', 'Maier'];
print namen[1]
telefon = {'Huber': 12345}
print telefon['Huber']
telefon['Maier'] = 54321
Listing 7
Ruby
namen = ['Huber', 'Maier'];
print namen[1]
telefon = {'Huber' => 12345}
print telefon['Huber']
telefon['Maier'] = 54321
Fallunterscheidungen
Da Listen wie auch Hashes die Speicherung nahezu beliebiger Typen ermöglicht, kann der Programmierer mit ihnen Daten für die meisten Anwendungsfälle speichern. Damit ist schon mal ein wesentlicher Teil jeder Programmierung erledigt: Schließlich geht es meistens nur darum, Daten einzulesen, sie zu verarbeiten und das Ergebnis auszugeben.
Im Verarbeitungsschritt werden so genannten Kontrollstrukturen wichtig. Sie ermöglichen dem Programmierer, den weiteren Verlauf des Programms vom Inhalt seiner Variablen abhängig zu machen. Der einfachste (und wohl auch häufigste) Fall einer Kontrollstruktur ist eine Fallunterscheidung: Enthält eine Variable diesen oder jenen Wert, soll das Programm unterschiedliche Dinge damit anstellen. In den Skript- und den meisten anderen Programmiersprachen steht dafür das Schlüsselwort if.
Angenommen, Sie wollten in ihrer Adressverwaltung nach allen Personen suchen, die in München wohnen, dann vergleichen Sie den Inhalt der fraglichen Variablen mit dem String “München”: if (wohnort == "München"). Die Bedingung steht in Perl, Python und Ruby stets in runden Klammern. Nach der Wenn-Bedingung folgt mindestens eine Anweisung oder ein Folge von Anweisungen, die der Interpreter ausführen soll, falls sie zutrifft.
Zusätzlich können Sie weitere Bedingungen anhängen, die abgefragt werden, falls die erste nicht zutrifft. Das erlaubt, ganze Ketten von Bedingung zu prüfen. Diese schließen üblicherweise mit einer Else-Anweisung ab, die Anweisungen für den Fall definiert, dass keine der geprüften Bedingungen zutraf.
Auch wenn es nicht unbedingt der normalen Denkweise entspricht, gelten für solche Bedingungen spezielle syntaktische Regeln. Schließlich besteht ein Gutteil der Programmierung stets darin, das eigene Denken dem Computer in eindeutiger Form mitzuteilen. Deshalb haben die Erfinder von Programmiersprachen für den Vergleich von Variablen die doppelten Gleichzeichen eingeführt. Anstelle der Zuweisung eines Werts mit einem einfachen Gleichzeichen führt das doppelte einen Vergleich durch.
Weil dies eine der häufigsten Fehlerquellen darstellt, sei noch einmal mit Nachdruck darauf hingewiesen: Sie können natürlich hier auch ein einfaches Gleichzeichen verwenden, denn auch das ist syntaktisch korrekt, doch es bedeutet etwas völlig anderes. Schreiben Sie if (wohnort = "München"), ist die abgefragte Bedingung immer wahr, denn Sie weisen einfach der Variablen wohnort den Wert München zu.
Blocks
An dieser Stelle ist es unumgänglich auf einen Grundunterschied zwischen Perl, Python und Ruby einzugehen, nämlich die Blockstruktur. Bei Blocks handelt es sich um Code-Bereiche, die der Interpreter in irgendeiner Weise als Ganzes behandelt. Damit lassen sich mehrere Anweisungen zu einer Einheit zusammenfassen.
Will der Programmierer zum Beispiel abhängig von einer If-Unterscheidung gleich mehrere Anweisungen ausführen lassen, kapselt er sie in einen Block. In Perl und Ruby dienen dazu geschweifte Klammern (wie in C), Ruby erlaubt zusätzlich noch die Schlüsselwörter begin und end (ähnlich zu Pascal). Python dagegen verfolgt ein eher ungewöhnliches Konzept, in dem die Einrückungstiefe der einzelnen Anweisungen die Zugehörigkeit zu einem Block festlegt. Die Listings 8 bis 10 zeigen die Unterschiede.
Listing 8
Perl
if ($name == "Huber") {
print "Herr Huber hat die Nummer ";
print $telefon['Huber'];
}
elsif ($name == "Maier") {
print "Herr Maier hat die Nummer ";
print $telefon['Maier'];
}
else {
print "Unbekannter Name";
}
Listing 9
Python
if (name == "Huber"):
print "Herr Huber hat die Nummer "
print telefon['Huber']
elif (name == "Maier"):
print "Herr Maier hat die Nummer "
print telefon['Maier']
else:
print "Unbekannter Name"
Listing 10
Ruby
if (name == "Huber") {
print "Herr Huber hat die Nummer "
print telefon['Huber']
}
elsif (name == "Maier") {
print "Herr Maier hat die Nummer "
print telefon['Maier']
}
else {
print "Unbekannter Name"
}
Wie man sieht, spart Python dadurch einige Klammern ein, allerdings muss der Programmierer genau auf jedes Leerzeichen achten. Zum Beispiel macht es einen Unterschied, ob er ein Tab-Zeichen verwendet oder mehrere Leerzeichen, obwohl der Code auf den ersten Blick gleich aussieht.
Schleifen
Ein Großteil jedes Programms besteht aus der wiederholten Anwendung von Anweisungen auf strukturell gleiche Daten. So besteht der Sinn von Adressanwendungen oft gerade darin, die vorhandenen Datensätze zu durchlaufen, und mit ihnen etwas anzustellen – zum Beispiel einen Serienbrief zu verschicken. Jede Skriptsprache bietet dazu verschiedene Schleifenkonstrukte. Der einfachste Fall ist eine For-Schleife, die einen Block von Anweisungen soundso oft durchläuft: “Mach’ das Folgende x-Mal”. In Perl sieht das so aus: for ($i = 0; $i <= 10; $i++). Ein folgender Block wird damit 11-Mal durchlaufen – die Zählung beginnt ja bei 0!
In Python und Ruby existieren Konstrukte, die ähnlich funktionieren, aber syntaktisch anders aufgebaut sind. Ruby realisiert eine solche Schleife mit for i in 0..10. Python benutzt dafür for i in range(11). Die Zahl 11 gibt im Unterschied zu den beiden anderen Sprachen nicht die Obergrenze der Zählung, sondern den größten Wert an.
Diese Schleifen setzen voraus, dass der Programmierer die Zahl der Durchläufe schon vorher kennt. bei von Listen und Hashes ist es aber häufig notwendig, einfach alle Elemente durchzugehen. Deshalb bieten die meisten Skriptsprachen dafür spezielle syntaktische Konstrukte wie foreach in Perl.
Objektorientiert vs. prozedural
An dieser Stelle zeigt sich ein Unterschied zwischen Perl und den beiden anderen vorgestellten Skriptsprachen, der bisher nicht zur Sprache kam: Python und Ruby sind von Grund auf objektorientiert. Nun lässt sich trefflich philosophieren, was Objektorientierung bedeutet, wir wollen es bei einigen Andeutungen belassen.
Die meisten Funktionen eines Programms sind bei der objektorientierten Programmierung an die Daten gebunden, die sie verarbeiten sollen. Diese Daten repräsentiert ein so genanntes Objekt. Im hier mehrfach verwendeten Adressbeispiel könnte es Objekte für Personen oder Adressen geben. Funktionen – oder im Objekt-Jargon: Methoden – könnten dann sein: das Anlegen, Verändern, Löschen von Adress- und Personendaten.
Weil Ruby noch mehr als Python auf Objektorientierung setzt, bietet es ein paar mehr ungewöhnliche Konstruktionen, die aber auch nichts anderes tun als ihre Python-Pendants. So ist es in Ruby nicht nötig, mit einer eingebauten Funktion foreach durch eine Liste zu wandern. Jede Liste besitzt, ererbt vom Elternobjekt, eine Methode each. Ein Ruby-Programmierer durchläuft eine Liste gerne mit liste.each { |elem| print elem }. Das in Pipe-Symbole eingeschlossene elem steht für das Listenelement beim jeweiligen Durchlauf. Viele Ruby-Objekte besitzen solche so genannten Iteratoren, weshalb man in Ruby-Code häufig auf solche Konstruktionen stößt.
Natürlich kann man mit Objektorientierung auch nichts anderes machen als mit der so genannten prozeduralen Programmierung wie in Perl, aber dieser Ansatz verhilft zumindest bei größeren Skriptprogramme zu übersichtlicherer Struktur und hoffentlich weniger Fehlern. Bei kleinen Skripts führt objektorientierte Programmierung eher zu mehr Schreibaufwand, aber auch in Python und Ruby lassen sich natürlich auch kleine Hacks schreiben.
Systemnähe
Die meisten schnellen Hacks mit Skriptsprachen unter Linux haben etwas damit zu tun, dass man zum Beispiel eine Datei einliest. Schließlich leisten die ganzen Variablen und Kontrollstrukturen erst etwas, wenn sie auch Daten zu verarbeiten haben. Deshalb ist jede Skriptsprache nur so gut wie ihre Bibliotheken, die umfassende Dienste für die Kommunikation mit dem Linux-System leisten: Sie müssen mindestens mit Dateien umgehen, Webseiten aus dem Internet holen, Regular Expressions beherrschen, XML-Dateien verstehen und vieles mehr.
Alle hier vorgestellten Skriptsprachen beherrschen die wichtigsten Anwendungsbereiche ausreichend gut. Das ist auch der Grund für ihre Popularität: Durch die Einfachheit von Variablen und Typen zusammen mit den umfangreichen Bibliotheken lassen sich mit wenigen Zeilen funktionsreiche Skripts schreiben.
Wie dieser Artikel zu zeigen versucht hat, bestehen die Unterschiede zwischen den Perl, Python und Ruby für einfach Anwendungsfälle nur in syntaktischen Feinheiten. Bei größeren Projekten sind Python und Ruby von Haus aus durch ihre Objektorientierung geeignete Kandidaten. Für welche Sprache man sich entscheidet, ist letztlich auch Geschmackssache
Zum Ausprobieren ist ein interaktiver Modus praktisch, wie ihn Python bietet, wenn Sie einfach nur den Interpreter python starten (Abbildung 1). Sie verlassen ihn wieder mit [Strg]+[D]. Bei Ruby dient dazu das separate Programm irb, das Sie über den Paketmanager nachinstallieren. wenn es fehlt. Für Perl gibt es leider nur die etwas unkomfortable Lösung, den Interpreter mit perl -de 42 im Debugging-Modus aufzurufen oder eine der Perl-Shells wie psh zu verwenden, die im Internet kursieren. Dort finden Sie auch zu jeder der drei vorgestellten Skriptsprachen eine Unmenge an Informationen für Anfänger und Fortgeschrittene. Lohnenswert ist auf jeden Fall ein Blick auf die PLEAC-Website, welche alle Beispiele des Perl Cookbook in vielen Sprachen nachbaut [4].
Glossar
-
Mono
-
Eine zu Microsofts .NET kompatible Entwicklungs- und Laufzeit-Umgebung für Software. Das Open-Source-Projekt wird hauptsächlich von Novell (Ximian) vorangetrieben.
-
Interpreter
-
Ein Programm, das einen Quellcode sondern einliest, zur Laufzeit analysiert und direkt ausführt. Im Gegensatz wandeln Assembler oder Compilern den Code in eine auf dem System direkt ausführbare Datei um.
-
Variablen
-
Speicherbereiche, in denen Werte gespeichert werden können. Der Inhalt wird über den Variablen-Namen angesprochen. Man unterscheidet Variablen nach ihrem Gültigkeitsbereich in lokale (gültig nur innerhalb eines Blocks) und globale (gültig im ganzen Programm) Variablen.
Infos
[1] Perl: http://www.perl.com
[2] Python: http://www.python.org
[3] Ruby: http://www.ruby-lang.org
[4] PLEAC: http://pleac.sourceforge.net







print “Die Summe ist “, $c;
würden viele Perlianer sicherlich eher als
print “Die Summe ist $c”
schreiben.
Man sieht, dass der Autor nicht mit Perl vertraut ist. Der Hauptvorteil von Perl, dass mit einem Minimum an Code ein Maximum an Wirkung erzielt werden kann. Diese Eigenschaft wird hier leider überhaupt nicht erwähnt.
Das ist schon ein ausgewogener, neutraler Vergleich. Ich kenne alle drei Sprachen bereits recht gut (insbesondere Perl), sodass er mir keine neuen Erkenntnisse gebracht hat. Für Python spricht klar seine Verbreitung, aber irgendwie spricht mich Ruby ästhetisch mehr an…