Wie kann ich Shell-Befehle aus einem Ruby-Programm heraus aufrufen? Wie bekomme ich dann die Ausgabe dieser Befehle zurück in Ruby?
Antworten
Zu viele Anzeigen?Sie können auch die Backtick-Operatoren (`) verwenden, ähnlich wie in Perl:
directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory
Praktisch, wenn Sie etwas Einfaches brauchen.
Welche Methode Sie verwenden möchten, hängt davon ab, was Sie genau erreichen wollen. In den Dokumenten finden Sie weitere Informationen zu den verschiedenen Methoden.
Anhand der hier gegebenen und in Mihais Antwort verlinkten Antworten habe ich eine Funktion erstellt, die diese Anforderungen erfüllt:
- Erfasst STDOUT und STDERR, so dass sie nicht "auslaufen", wenn mein Skript über die Konsole ausgeführt wird.
- Erlaubt die Übergabe von Argumenten an die Shell als Array, so dass man sich keine Gedanken über Escaping machen muss.
- Erfasst den Exit-Status des Befehls, damit klar ist, wann ein Fehler aufgetreten ist.
Als Bonus gibt dieser Befehl auch STDOUT zurück, wenn der Shell-Befehl erfolgreich beendet wird (0) und etwas auf STDOUT ausgibt. Auf diese Weise unterscheidet sie sich von system
der einfach Folgendes zurückgibt true
in solchen Fällen.
Der Code folgt. Die spezifische Funktion ist system_quietly
:
require 'open3'
class ShellError < StandardError; end
#actual function:
def system_quietly(*cmd)
exit_status=nil
err=nil
out=nil
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
err = stderr.gets(nil)
out = stdout.gets(nil)
[stdin, stdout, stderr].each{|stream| stream.send('close')}
exit_status = wait_thread.value
end
if exit_status.to_i > 0
err = err.chomp if err
raise ShellError, err
elsif out
return out.chomp
else
return true
end
end
#calling it:
begin
puts system_quietly('which', 'ruby')
rescue ShellError
abort "Looks like you don't have the `ruby` command. Odd."
end
#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
Vergessen Sie nicht die spawn
um einen Hintergrundprozess zur Ausführung des angegebenen Befehls zu erstellen. Sie können sogar auf dessen Beendigung warten, indem Sie die Process
Klasse und der zurückgegebenen pid
:
pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid
pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid
Der Arzt sagt: Diese Methode ist vergleichbar mit #system
aber er wartet nicht, bis der Befehl beendet ist.
Wir können dies auf verschiedene Weise erreichen.
Verwendung von Kernel#exec
nach der Ausführung dieses Befehls nichts mehr:
exec('ls ~')
Verwendung von backticks or %x
`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"
Verwendung von Kernel#system
Befehl, gibt zurück true
wenn erfolgreich, false
wenn er nicht erfolgreich war und gibt nil
wenn die Befehlsausführung fehlschlägt:
system('ls ~')
=> true
4 Stimmen
Diese Frage ist zwar nützlich, aber sie wird nicht gut gestellt. Ruby hat viele Möglichkeiten, Sub-Shells aufzurufen, die gut dokumentiert sind und leicht durch Lesen der Kernel y Öffnen3 Dokumentation und Suche hier auf SO.
3 Stimmen
Leider ist dieses Thema sehr komplex.
Open3
( docs ) ist die beste Wahl für die meisten Situationen, IMO, aber auf älteren Versionen von Ruby, wird es nicht respektieren eine modifiziertePATH
( bugs.ruby-lang.org/issues/8004 ), und je nachdem, wie Sie args übergeben (insbesondere, wenn Sie opts hash mit Nicht-Schlüsselwörtern verwenden), kann es zu einem Bruch kommen. Aber, wenn Sie diese Situationen treffen, dann tun Sie etwas ziemlich fortgeschritten und Sie können herausfinden, was zu tun ist, indem Sie die Implementierung vonOpen3
.6 Stimmen
Ich bin überrascht, dass niemand erwähnt hat
Shellwords.escape
( doc ). Sie möchten keine Benutzereingaben direkt in Shell-Befehle einfügen - geben Sie sie zuerst in einer Escape-Funktion ein! Siehe auch Befehlsinjektion .