Die Programmiersprache Ruby (German Edition)
Methodenaufruf in einem
os = %x{uname} # Eine andere Quoting-Syntax
os = Kernel.`("uname") # Die Methode explizit aufrufen
Diese Methode ruft nicht einfach das angegebene Executable auf; sie startet eine Shell, so dass Shell-Features wie die Platzhalterersetzung in Dateinamen verfügbar sind:
files = `echo *.xml`
Eine weitere Möglichkeit, einen Prozess zu starten und seine Ausgabe zu lesen, bietet die Funktion
Kernel.open
. Diese Methode ist eine Variante von
File.open
und wird am häufigsten zum Öffnen von Dateien verwendet. (Und wenn Sie
require 'open-uri'
aus der Standardbibliothek aufrufen, kann sie auch zum Öffnen von HTTP- und FTP-URLs verwendet werden.) Aber wenn das erste Zeichen des angegebenen »Dateinamens« das Pipe-Zeichen
|
ist, öffnet sie stattdessen eine Pipe, um aus dem angegebenen Shell-Kommando zu lesen und/oder um hineinzuschreiben:
pipe = open("|echo *.xml")
files = pipe.readline
pipe.close
Wenn Sie ein Kommando in einer Shell aufrufen möchten, sich aber nicht für seine Ausgabe interessieren, sollten Sie besser die Methode
Kernel.system
verwenden. Wenn ihr ein einzelner String übergeben wird, führt sie diesen String in einer Shell aus, wartet auf den Abschluss des Befehls und gibt
true
bei Erfolg oder
false
bei Misserfolg zurück. Wenn Sie mehrere Argumente an
system
übergeben, ist das erste Argument der Name des aufzurufenden Programms, und die restlichen Argumente sind dessen Kommandozeilenargumente. In diesem Fall wird keine Shell-Erweiterung für diese Argumente ausgeführt.
Auf einer niedrigeren Ebene besteht die Möglichkeit, ein beliebiges Executable mithilfe der Funktion
exec
auszuführen. Diese Funktion kehrt nie zurück: Sie ersetzt den zurzeit laufenden Ruby-Prozess einfach durch das angegebene Executable. Dies könnte nützlich sein, wenn Sie ein Ruby-Skript schreiben, das einfach ein Wrapper zum Starten eines anderen Programms ist. Normalerweise wird die Funktion allerdings in Zusammenarbeit mit der im nächsten Abschnitt vorgestellten Funktion
fork
verwendet.
10.4.2 Forking und Prozesse
In „9.9 Threads und Parallelität“ wurde Rubys API zum Schreiben von Multithreading-Programmen beschrieben. Ein anderer Ansatz, um in Ruby Nebenläufigkeit zu erreichen, besteht darin, mehrere Ruby-Prozesse zu verwenden. Erledigen Sie dies mit der Funktion
fork
oder ihrem Synonym
Process.fork
. Die einfachste Möglichkeit besteht darin, diese Funktion mit einem Block zu verwenden:
fork {
puts "Hallo vom Child-Prozess: #$$"
}
puts "Hallo vom Parent-Prozess: #$$"
Bei dieser Verwendungsart fährt der ursprüngliche Ruby-Prozess mit dem Code fort, der hinter dem Block steht, und der neue Ruby-Prozess führt den Code im Block aus.
Bei einem Aufruf ohne Block verhält
fork
sich anders. Im Parent-Prozess gibt der Aufruf von
fork
einen Integer zurück, der die Prozess-ID des neu erzeugten Child-Prozesses ist. Im Child-Prozess gibt derselbe Aufruf von
fork
den Wert
nil
zurück. Deshalb kann der obige Code auch so geschrieben werden:
pid = fork
if (pid)
puts "Hallo vom Parent-Prozess: #$$"
puts "Child-Prozess #{pid} erzeugt"
else
puts "Hallo vom Child-Prozess: #$$"
end
Ein sehr wichtiger Unterschied zwischen Prozessen und Threads besteht darin, dass Prozesse sich keinen Speicher teilen. Wenn Sie
fork
aufrufen, beginnt der neue Ruby-Prozess als exaktes Duplikat des Parent-Prozesses. Aber jegliche Änderungen, die er am Prozesszustand ausführt (durch das Ändern oder Erzeugen von Objekten), finden in seinem eigenen Adressraum statt. Weder kann der Child-Prozess die Datenstrukturen des Parent-Prozesses ändern, noch kann der Parent-Prozess die Strukturen modifizieren, die der Child-Prozess sieht.
Wenn Sie eine Möglichkeit brauchen, Ihre Parent- und Child-Prozesse miteinander kommunizieren zu lassen, verwenden Sie
open
und übergeben Sie »
|-
« als erstes Argument. Dies öffnet eine Pipe zu einem neu erzeugten Ruby-Prozess. Der
open
-Aufruf führt sowohl im Parent- als auch im Child-Prozess den angegebenen Block aus. Im Child empfängt der Block
nil
. Im Parent wird dagegen ein
IO
-Objekt an den Block übergeben. Das Lesen aus diesem
IO
-Objekt gibt Daten zurück, die der Child-Prozess geschrieben hat. Und Daten, die in das
IO
-Objekt geschrieben werden, stehen dem Child-Prozess zum Lesen durch seine Standardeingabe zur Verfügung, zum Beispiel:
open("|-", "r+") do |child|
if child
# Dies ist der Parent-Prozess.
child.puts("Hallo Kind") # An Child senden
response = child.gets #
Weitere Kostenlose Bücher