Programmierung in Ruby

Der Leitfaden der Pragmatischen Programmierer

Ruby und seine Welt



So unschön es ist, es ist doch eine Tatsache, dass unsere Applikationen es mit der großen, bösen Welt zu tun kriegen. In diesem Kapitel sehen wir uns an, wie Ruby mit seiner Umgebung interagiert. Microsoft-Windows-Benutzer sollten sich auch die plattform-spezifischen Informationen ab Seite 165 ansehen.

Kommandozeilen-Argumente

``In the Beginning was the Command Line.'' (Am Anfang war die Kommandozeile)[Titel eines wunderbaren Essays von Neal Stephenson (verfügbar bei http://www.spack.org/essays/commandline.html).] Unabhängig vom System, auf dem Ruby eingesetzt wird, egal ob eine wissenschaftliche Super-High-End-Graphik-Workstation oder ein embedded PDA-Gerät, man muss den Ruby Interpreter irgendwie starten und das gibt einem Gelegenheit, ihm Kommanozeilen-Argumente mitzugeben.

Eine Ruby-Kommandozeile besteht aus drei Teilen: Optionen für den Ruby-Interpreter, optional den Namen des auszuführenden Programms und optional ein Satz von Argumenten für dieses Programm.

ruby [options][--][programfile][arguments]

Die Ruby-Optionen enden bei dem ersten Wort der Kommandozeile, das nicht mit einem Bindestrich anfängt oder bei der speziellen Option ``--'' (zwei Bindestriche).

Wenn es keinen Dateinamen in der Kommandozeile gibt oder wenn er aus einem einzelnen Bindestrich besteht (-), dann liest Ruby den Quellcode für sein Programm vom Standard-Input.

Argumente für das Programm selber kommen nach dem Programmnamen. Als Beispiel:

% ruby -w - "Hello World"

Das schaltet die Warnungen an, liest das Programm vom Standard-Input und übergibt ihm den String "Hello World" als Argument.

Kommandozeilen-Optionen

-0[octal}
' Das ``0''-Flag beschreibt das Record-Separations-Zeichen (\0, falls keine Ziffern folgen). -00 bedeutet Paragraph-Modus: Records werden durch zwei Default-Record-Separations-Zeichen getrennt. -0777 liest die komplette Datei auf einmal ein (weil das ein illegales Zeichen ist). Verändert $/.

-a
' Auto-Split Modus, falls zusammen mit -n or -p benutzt; ist äquivalent zur Ausführung von {$F am Anfang jedes Schleifendurchgangs.

-C directory
' Ändert vor der Ausführung das Arbeits-Verzeichnis auf directory.

-c
' Checkt nur die Syntax; führt das Programm nicht aus.

--copyright
gibt den Copyright-Hinweis aus und beendet das Programm.

-d, --debug
' Setzt $DEBUG auf true. Damit ist das Programm in der Lage, zusätzliches Tracing zu nutzen.

-e 'command'
' Führt command als eine Zeile des Quellcodes aus. Mehrere -e's sind erlaubt und die Kommandos werden wie mehrere Zeilen des selben Programms behandelt. Falls programfile fehlt aber -e vorhanden ist, hört die Ausführung des Programms auf, wenn die -e-Kommandos beendet sind.

-F pattern
' Gibt den Feld-Seperator ($;) für den Input an, der als Default-Wert für split() benutzt wird (beeinflusst -a).

-h, --help
' Zeigt einen kurzen Hilfe-Bildschirm an.

-I directories
' Gibt Verzeichnisse an, die vorne an $LOAD_PATH ($:) angehängt werden sollen. Mehrere -I-Optionen dürfen vorhanden sein und mehrere Verzeichnisse dürfen unter jedem -I stehen. Die Verzeichnisse werden dann auf unix-ähnlichen Systemen mit ``:'' und auf DOS/Windows-Systemen mit ``;'' getrennt.

-i [extension}
' Beschreibt die ARGV-Dateien, falls vorhanden. In jede der in ARGV genannten Dateien wird alles, was man auf den Standard-Output schreibt, als Inhalt dieser Datei gesichert. Vorher wird eine Sicherheitskopie der Datei angelegt, falls extension angegeben ist.
% ruby -pi.bak -e "gsub(/Perl/, 'Ruby')" *.txt

-K kcode
' Beschreibt den zu benutzenden Code-Satz. Das ist vor allem dann nützlich, wenn Ruby japanisch geschriebene Dateien ausführen soll. kcode kann folgendermaßen aussehen: e, E für EUC; s, S für SJIS; u, U für UTF-8 oder a, A, n, N für ASCII.

-l
' Schaltet das automatische Zeilenende-Processing an; setzt $\ auf den Wert von $/ und kappt jede Input-Zeile automatisch.

-n
' Erzeugt eine ``while gets; ...; end''-Schleife um das Programm. Beispielsweise wird ein einfaches grep-Kommando zu:
% ruby -n -e "print if /wombat/"  *.txt

-p
' Setzt den Programm-Code in eine ``while gets; ...; print; end''-Schleife.
% ruby -p -e "$_.downcase!" *.txt

-r library
' Benötigt die genannte Bibliothek library vor der Ausführung.

-S
' Sucht die Programm-Datei unter den Pfaden in den RUBYPATH oder PATH Umgebungs-Variablen.

-s
' Alle Kommandozeilen-Optionen, die zwischen dem Programm-Namen und irgenwelchen Dateinamens-Argumenten oder einem -- stehen, werden von ARGV entfernt und stattdessen als globale Variable mit dem Namen dieser Option gesetzt. In dem folgenden Beispiel wird damit die Variable $opt auf ``electric'' gesetzt.

% ruby -s prog -opt=electric ./mydata

-T[level}
' Setzt den Sicherheits-Level, der unter anderem den Tainting-Check ermöglicht (siehe Seite 257). Setzt $SAFE.

-v, --verbose
' Schaltet in den Verbose-Modus und gibt die Versions-Nummer aus. Im Verbose-Modus werden Compiler-Warnungen ausgegeben. Falls kein Programm-Name in der Kommandozeile auftaucht, beendet sich Ruby.

--version
Zeigt die Ruby-Versions-Nummer an und beendet sich.

-w
' Schaltet den Verbose-Modus an. Anders als -v wird hier das Programm vom Standard-Input gelesen, wenn keine Programmdatei in der Kommandozeile steht. Wir empfehlen, Ruby-Programme immer mit -w laufen zu lassen.

-X directory
' Ändert vor der Ausführung das Arbeitsverzeichnis auf directory. Dasselbe wie -C directory.

-x [directory}
' Schneidet den Text vor der #!ruby-Zeile ab und ändert das Arbeitsverzeichnis auf directory falls angegeben.

-y, --yydebug
' Schaltet das yacc-Debugging im Parser an (das ergibt einen Berg an Informationen).

ARGV

Alle Kommandozeilen-Argumente nach dem Programmnamen sind im Ruby-Programm in dem globalen Array ARGV verfügbar. Wenn man zum Beispiel Ruby mit

% ruby -w ptest "Hello World" a1 1.6180

aufruft, ergibt das ein ARGV-Array mit dem Inhalt ["Hello World", a1, 1.6180]. Da gibt es eine kleine Falle für all die C-Programmierer --- ARGV[0] ist das erste Argument, nicht der Programmname. Der Name des aktuellen Programms ist in der globalen Variablen $0 verfügbar.

Programm-Terminierung

Die Methode Kernel#exit beendet Ihr Programm und gibt einen Zustands-Wert an das Betriebssystem zurück. Anders als bei einigen anderen Sprachen wird mit exit das Programm nicht nur einfach abgebrochen. Kernel#exit löst erst eine SystemExit-Exception aus, die man wenn man will auffangen kann, und führt noch mehrere Aufräumarbeiten durch, unter anderem das Ausführen aller registrierten at_exit-Methoden und Objekt-Finalizers. Einzelheiten gibts bei Kernel#exit ab Seite 419.

Umgebungs-Variablen

Auf Umgebungs-Variablen des Betriebssystems kann man über die vordefinierte Variable ENV zugreifen. Diese reagiert auf dieselben Methoden wie ein Hash. [ENV ist nicht wirklich ein Hash, aber falls nötig kann an das in einen Hash mit ENV#to_hash konvertieren.]

Die Werte von einigen Umgebungs-Variablen werden von Ruby beim ersten Start gelesen. Diese Variablen verändern das Verhalten des Interpreters, so wie in Tabelle 13.1 auf Seite 141 beschrieben.
Von Ruby benutzte Umgebungs-Variablen
Variablen-Name Beschreibung
RUBYOPT Zusätzliche Kommandozeilen-Optionen für Ruby; werden ausgewertet nachdem die echten Kommandozeilen-Optionen ausgewertet sind($SAFE muss dafür 0 sein).
RUBYLIB Zusätzlicher Such-Pfad für Ruby-Programme ($SAFE muss dafür 0 sein).
RUBYPATH Zusammen mit der -S-Option: Suchpfad für die Ruby-Programme (defaultmäßig PATH).
RUBYSHELL Shell, die beim Erzeugen eines Prozesses benutzt wird; falls nicht gesetzt wird SHELL oder COMSPEC benutzt.
DLN_LIBRARY_PATH Suchpfad für dynamisch geladene Module.
RUBYLIB_PREFIX (Nur Windows) Baut den RUBYLIB-Suchpfad zusammen, indem vor jede Komponente dieser Prefix gehängt wird.

Umgebungsvariablen schreiben

Ein Rupy-Programm kann auf das ENV-Objekt schreiben, das ändert auf den meisten Systemen auch den Wert der dazugehörenden Umgebungsvariablen. Allerdings ist diese Änderung nur lokal zu dem ändernden Prozess und zu allen danach abgeleiteten Kindprozessen. Diese Vererbung von Umgebungsvariablen wird in dem folgenden Code gezeigt. Ein Unterprozess ändert eine Umgebungs-Variable und diese Änderung ist in einem von ihm danach gestarteten Prozess sichtbar. Allerdings ist diese Änderung nicht im ursprünglichen Elternprozess sichtbar. (Das zeigt mal wieder, dass Eltern niemals wirklich wissen, was ihre Kinder anstellen.)

puts "In parent, term = #{ENV['TERM']}"
fork do
  puts "Start of child 1, term = #{ENV['TERM']}"
  ENV['TERM'] = "ansi"
  fork do
    puts "Start of child 2, term = #{ENV['TERM']}"
  end
  Process.wait
  puts "End of child 1, term = #{ENV['TERM']}"
end
Process.wait
puts "Back in parent, term = #{ENV['TERM']}"
erzeugt:
In parent, term = xterm
Start of child 1, term = xterm
Start of child 2, term = ansi
End of child 1, term = ansi
Back in parent, term = xterm

Wo Ruby seine Module findet

Man benutzt require oder load, um ein Bibliotheks-Modul in seinem Ruby-Programm zu nutzen. Einige dieser Module werden mit Ruby mitgeliefert, einige hat man abseits des Ruby-Application-Archivs installiert und einige hat man selber geschrieben. Wie findet Ruby die alle?

Wenn Ruby auf einer speziellen Maschine installiert wird, definiert es sich erstmal einen Satz von Verzeichnissen, in die der ganze Bibliothekskram reinkommt. Wo diese sind, hängt von dem speziellen System ab. Man kann sie über die Kommandozeile herausfinden mit:

% ruby -e 'puts $:'

Auf einer der üblichen Linux-Kisten sieht das dann ungefähr so aus:

/usr/local/lib/ruby/site_ruby/1.6/i686-linux
/usr/local/lib/ruby/site_ruby/1.6
/usr/local/lib/ruby/site_ruby
/usr/local/lib/ruby/1.6/i686-linux
/usr/local/lib/ruby/1.6
.

Die site_ruby-Verzeichnisse enthalten Module und Erweiterungen, die man selber hinzugefügt hat. Die architektur-abhängigen Verzeichnisse (in diesem Fall i686-linux) enthalten ausführbare Dateien und andere für diese spezielle Maschine spezifische Sachen. All diese Verzeichnisse werden automatisch von Ruby bei der Suche nach Modulen bedacht.

Manchmal reicht das nicht. Vielleicht arbeitet man an einem großen Projekt und hat zusammen mit seinen Kollegen eine wichtige Bibliothek in Ruby geschrieben. Jeder aus dem Team soll Zugriff auf diesen ganzen Code haben. Es gibt eine Reihe von Möglichkeiten, um dies zu erreichen. Wenn das Programm mit dem Sicherheits-Level 0 (siehe Kapitel 20 ab Seite 257) läuft, kann man in die Umgebungsvariable RUBYLIB eine Liste von einem oder mehreren zu durchsuchenden Verzeichnissen schreiben.[Das Trennzeichen zwischen diesen Verzeichnissen hängt vom System ab. Bei Windows ist es ein Semicolon; bei Unix ein Doppelpunkt.] Wenn das Programm nicht mit setuid läuft, dann kann man den Kommandozeilen-Parameter -I nutzen, um daselbe zu bewirken.

Schließlich ist die Ruby-Variable $: ein Array von Orten, an denen nach geladenen Dateien gesucht wird. Diese Variable wird mit der Liste der Standard-Verzeichnisse angelegt, dazu kommen diejenigen, die man mit RUBYLIB und -I noch angibt. Man kann jederzeit zusätzliche Verzeichnisse aus dem laufenden Programm zu diesem Array hinzufügen.

Build-Umgebung

Während Ruby für ein spezielles System kompiliert wird, werden alle beim Kompilieren relevanten Einstellungen (inklusive die Architektur der Maschine, auf der kompiliert wird, Kompiler Optionen, Quell-Code-Verzeichnis und so weiter) in das Modul Config innerhalb der Bibliotheks-Datei ``rbconfig.rb'' geschrieben. Nach der Installation kann jedes Ruby-Programm dieses Modul nutzen, um zu erfahren, wie Ruby kompiliert wurde.

require "rbconfig.rb"
include Config
CONFIG["host"] » "i686-pc-linux"
CONFIG["LDFLAGS"] » "-rdynamic"

Erweiterungsbibliotheken benutzen diese Konfigurationsdatei, um auf einer gegebenen Architektur geeignet zu kompilieren und zu linken. Für Einzelheiten siehe Kapitel 17 ab Seite 171 und die Erläuterungen zu mkmf ab Seite 455.


Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Übersetzung: Jürgen Katins
Für das englische Original:
© 2000 Addison Wesley Longman, Inc. Released under the terms of the Open Publication License V1.0. That reference is available for download.
Diese Lizenz sowie das Original vom Herbst 2001 bilden die Grundlage der Übersetzung
Es wird darauf hingewiesen, dass sich die Lizenz des englischen Originals inzwischen geändert hat.
Für die deutsche Übersetzung:
© 2002 Jürgen Katins
Der Copyright-Eigner stellt folgende Lizenzen zur Verfügung:
Nicht-freie Lizenz:
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder. Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.
Freie Lizenz:
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".