Frage Aufruf von Shell-Befehlen von Ruby


Wie rufe ich Shell-Befehle innerhalb eines Ruby-Programms auf? Wie bekomme ich dann von diesen Befehlen wieder zurück in Ruby?


883
2017-08-05 12:56


Ursprung


Antworten:


Diese Erklärung basiert auf einem Kommentar Ruby-Skript von einem Freund von mir. Wenn Sie das Skript verbessern möchten, können Sie es unter dem Link aktualisieren.

Beachten Sie zunächst, dass Ruby, wenn es eine Shell aufruft, normalerweise aufruft /bin/sh, nicht Bash. Einige Bash-Syntax wird von nicht unterstützt /bin/sh auf allen Systemen.

Hier sind Möglichkeiten, ein Shell-Skript auszuführen:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , allgemein Backticks genannt - `cmd`

    Dies ist wie viele andere Sprachen, einschließlich Bash, PHP und Perl.

    Gibt das Ergebnis des Shell-Befehls zurück.

    Dokumente: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Eingebaute Syntax, %x( cmd )

    Nach dem x Zeichen ist ein Trennzeichen, das ein beliebiges Zeichen sein kann. Wenn das Trennzeichen eines der Zeichen ist (, [, {, oder <, das Literal besteht aus den Zeichen bis zum passenden schließenden Begrenzer, Berücksichtigung geschachtelter Delimiter-Paare. Für alle anderen Trennzeichen gilt Literal umfasst die Zeichen bis zum nächsten Auftreten des Trennzeichen. String-Interpolation #{ ... } ist erlaubt.

    Gibt das Ergebnis des Shell-Befehls zurück, genau wie die Backticks.

    Dokumente: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    Führt den angegebenen Befehl in einer Subshell aus.

    Kehrt zurück true Wenn der Befehl gefunden wurde und erfolgreich ausgeführt wurde, false Andernfalls.

    Dokumente: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    Ersetzt den aktuellen Prozess durch Ausführen des angegebenen externen Befehls.

    Gibt keine zurück, der aktuelle Prozess wird ersetzt und wird nie fortgesetzt.

    Dokumente: http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

Hier einige zusätzliche Ratschläge: $?, das ist das gleiche wie $CHILD_STATUS, greift auf den Status des letzten vom System ausgeführten Befehls zu, wenn Sie die Backticks verwenden, system() oder %x{}. Sie können dann auf die exitstatus und pid Eigenschaften:

$?.exitstatus

Für mehr lesen siehe:


1158
2017-08-05 14:42



Die Art, wie ich das mache, ist die Verwendung der %x Literal, was es einfach (und lesbar!) macht, Anführungszeichen in einem Befehl zu verwenden:

directorylist = %x[find . -name '*test.rb' | sort]

In diesem Fall füllt die Dateiliste alle Testdateien im aktuellen Verzeichnis, die Sie wie erwartet verarbeiten können:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

151
2017-08-05 14:08



Hier ist ein Flussdiagramm basierend auf diese Antwort. Siehe auch, verwenden script ein Terminal zu emulieren.

enter image description here


148
2018-05-19 17:01



Hier ist der beste Artikel meiner Meinung nach zum Ausführen von Shell-Skripten in Ruby: "6 Möglichkeiten zum Ausführen von Shell-Befehlen in Ruby".

Wenn Sie nur die Ausgabe benötigen, verwenden Sie Backticks.

Ich brauchte fortgeschrittenere Sachen wie STDOUT und STDERR, also benutzte ich das Open4-Juwel. Sie haben alle Methoden dort erklärt.


58
2017-09-02 11:05



Mein Favorit ist Öffnen3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

31
2017-09-18 17:47



Einige Dinge, über die Sie bei der Wahl zwischen diesen Mechanismen nachdenken sollten, sind:

  1. Willst du nur stdout oder tust du? Brauchst du auch stderr? oder auch ausgesondert?
  2. Wie groß ist dein Output? Möchtest du das gesamte Ergebnis im Speicher zu halten?
  3. Willst du etwas von dir lesen? Ausgabe, während der Subprozess noch läuft Laufen?
  4. Benötigen Sie Ergebniscodes?
  5. Brauchen Sie ein Rubin-Objekt? stellt den Prozess dar und lässt Sie Töte es auf Nachfrage?

Sie können etwas von einfachen Backticks (``), System () und IO.popen zu ausgewachsen Kernel.fork/Kernel.exec mit IO.pipe und IO.select.

Sie können auch Timeouts in die Mischung werfen, wenn ein Unterprozess zu lange dauert.

Leider sehr viel hängt davon ab.


23
2017-08-07 05:10



Eine weitere Option:

Wenn du:

  • brauche stderr sowie stdout
  • kann / werde nicht Open3 / Open4 benutzen (sie werfen Ausnahmen in NetBeans auf meinem Mac, keine Ahnung warum)

Sie können die Shell-Umleitung verwenden:

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

Das 2>&1 Syntax funktioniert übergreifend Linux, Mac und Windows seit den frühen Tagen von MS-DOS.


19
2018-06-16 02:13



Ich bin definitiv kein Ruby-Experte, aber ich werde es versuchen:

$ irb 
system "echo Hi"
Hi
=> true

Sie sollten auch in der Lage sein, Dinge zu tun wie:

cmd = 'ls'
system(cmd)

16
2017-08-05 13:24



Die obigen Antworten sind schon ziemlich gut, aber ich möchte den folgenden zusammenfassenden Artikel wirklich teilen: "6 Möglichkeiten zum Ausführen von Shell-Befehlen in Ruby"

Im Grunde sagt es uns:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system und $?:

system 'false' 
puts $?

Backticks (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -- ein Edelstein:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

12
2018-06-07 02:07



Wenn Sie wirklich Bash brauchen, per Note in der "besten" Antwort.

Beachten Sie zunächst, dass Ruby, wenn es eine Shell aufruft, normalerweise aufruft /bin/sh, nicht Bash. Einige Bash-Syntax wird von nicht unterstützt /bin/sh auf allen Systemen.

Wenn Sie Bash verwenden müssen, fügen Sie ein bash -c "your Bash-only command" innerhalb Ihrer gewünschten Anrufmethode.

quick_output = system("ls -la")

quick_bash = system("bash -c 'ls -la'")

Zu testen:

system("echo $SHELL") system('bash -c "echo $SHELL"')

Oder wenn Sie eine vorhandene Skriptdatei (z. B. script_output = system("./my_script.sh")) Rubin sollte ehre den Shebang, aber du könntest immer benutzen system("bash ./my_script.sh") um sicher zu gehen (obwohl es vielleicht einen leichten Overhead von /bin/sh Laufen /bin/bashDas wirst du wahrscheinlich nicht bemerken.


8
2018-06-02 20:14