Frage

Wie rufe ich Shell-Befehle innerhalb eines Ruby-Programms auf?Wie erhalte ich dann die Ausgabe dieser Befehle zurück in Ruby?

War es hilfreich?

Lösung

Diese Erklärung basiert auf einem kommentierten Ruby-Skript von einem Freund von mir.Wenn Sie das Skript verbessern möchten, können Sie es gerne über den Link aktualisieren.

Beachten Sie zunächst, dass Ruby normalerweise eine Shell 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 bei vielen anderen 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. Integrierte Syntax, %x( cmd )

    Im Anschluss an die x Zeichen ist ein Trennzeichen, das ein beliebiges Zeichen sein kann.Wenn das Trennzeichen eines der Zeichen ist (, [, {, oder <Das Literal besteht aus den Charakteren bis zum passenden Abschluss, der verschachtelte Trennzeichenpaare berücksichtigt.Für alle anderen Grenzwerte umfasst das Literal die Charaktere bis zum nächsten Ereignis des Trennzeichencharakters.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/sprache.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 und erfolgreich ausgeführt wurde, false ansonsten.

    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 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 noch ein paar zusätzliche Ratschläge:$?, was dasselbe ist wie $CHILD_STATUS, greift auf den Status des zuletzt vom System ausgeführten Befehls zu, wenn Sie die Backticks verwenden, system() oder %x{}.Anschließend können Sie darauf zugreifen exitstatus Und pid Eigenschaften:

$?.exitstatus

Weitere Informationen finden Sie unter:

Andere Tipps

Hier ist ein Flussdiagramm, das darauf basiert diese Antwort.Siehe auch, verwenden script um ein Terminal zu emulieren.

enter image description here

Am liebsten mache ich das mit der %x Literal, das die Verwendung von Anführungszeichen in einem Befehl einfach (und lesbar!) macht, etwa so:

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

In diesem Fall wird die Dateiliste mit allen Testdateien im aktuellen Verzeichnis gefüllt, die Sie wie erwartet verarbeiten können:

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

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

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

Ich brauchte fortgeschrittenere Dinge wie STDOUT und STDERR, also habe ich das Open4-Juwel verwendet.Dort sind alle Methoden erklärt.

Mein Favorit ist Öffnen3

  require "open3"

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

Bei der Wahl zwischen diesen Mechanismen sollten Sie Folgendes bedenken:

  1. Willst du nur Stdout oder brauchst du auch Stderr?oder sogar getrennt?
  2. Wie groß ist Ihr Output?Möchten Sie das gesamte Ergebnis im Speicher halten?
  3. Möchten Sie einen Teil Ihrer Ausgabe lesen, während der Subprozess noch ausgeführt wird?
  4. Benötigen Sie Ergebniscodes?
  5. Benötigen Sie ein Ruby -Objekt, das den Prozess repräsentiert und Sie ihn bei Bedarf töten lassen?

Möglicherweise benötigen Sie alles von einfachen Backticks (``), system() und IO.popen bis zur Vollendung Kernel.fork/Kernel.exec mit IO.pipe Und IO.select.

Möglicherweise möchten Sie auch Zeitüberschreitungen hinzufügen, wenn die Ausführung eines Unterprozesses zu lange dauert.

Leider sehr viel kommt darauf an.

Noch eine Möglichkeit:

Wenn du:

  • Benötige sowohl stderr als auch stdout
  • kann/will Open3/Open4 nicht verwenden (sie lösen Ausnahmen in NetBeans auf meinem Mac aus, 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"

Der 2>&1 Die Syntax funktioniert quer Linux, Mac und Windows seit den Anfängen von MS-DOS.

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

$ irb 
system "echo Hi"
Hi
=> true

Sie sollten auch in der Lage sein, Folgendes zu tun:

cmd = 'ls'
system(cmd)

Die Antworten oben sind schon ganz toll, aber ich möchte unbedingt den folgenden zusammenfassenden Artikel teilen:"6 Möglichkeiten, Shell-Befehle in Ruby auszuführen"

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>]

Wenn Sie Bash wirklich benötigen, lesen Sie den Hinweis in der „besten“ Antwort.

Beachten Sie zunächst, dass Ruby normalerweise eine Shell 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 Aufrufmethode.

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 ausführen (z. B script_output = system("./my_script.sh")) Rubin sollen Ehre den Schebang, aber du könntest ihn immer gebrauchen system("bash ./my_script.sh") um sicherzugehen (es kann jedoch zu einem leichten Mehraufwand kommen). /bin/sh läuft /bin/bash, Sie werden es wahrscheinlich nicht bemerken.

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 benötigen.

Welche Methode Sie verwenden möchten, hängt davon ab, was Sie genau erreichen möchten.Weitere Informationen zu den verschiedenen Methoden finden Sie in den Dokumenten.

Der einfachste Weg ist zum Beispiel:

reboot = `init 6`
puts reboot

Anhand der Antworten hier und der Verknüpfung in Mihais Antwort habe ich eine Funktion zusammengestellt, die diese Anforderungen erfüllt:

  1. Erfasst STDOUT und STDERR sauber, damit sie nicht „durchsickern“, wenn mein Skript von der Konsole ausgeführt wird.
  2. Ermöglicht die Übergabe von Argumenten als Array an die Shell, sodass Sie sich keine Sorgen über Escapezeichen machen müssen.
  3. Erfasst den Exit-Status des Befehls, sodass klar ist, wann ein Fehler aufgetreten ist.

Als Bonus gibt dieser auch STDOUT zurück, wenn der Shell-Befehl erfolgreich beendet wird (0) und irgendetwas auf STDOUT setzt.In dieser Hinsicht unterscheidet es sich von system, was einfach zurückkehrt true in solchen Fällen.

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"

Wir können es auf verschiedene Arten erreichen.

Benutzen Kernel#exec, nichts, nachdem dieser Befehl ausgeführt wurde:

exec('ls ~')

Benutzen backticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

Benutzen Kernel#system Befehl, kehrt zurück true Falls erfolgreich, false wenn erfolglos und kehrt zurück nil Wenn die Befehlsausführung fehlschlägt:

system('ls ~')
=> true

Vergiss das nicht spawn Befehl zum Erstellen eines Hintergrundprozesses zum Ausführen des angegebenen Befehls.Sie können sogar auf den Abschluss warten, indem Sie die Funktion verwenden Process Klasse und die zurückgegebene 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 ähnelt #system aber es wartet nicht, bis der Befehl beendet ist.

Wenn Sie einen komplexeren Fall als den allgemeinen Fall haben (der nicht bearbeitet werden kann). ``) dann schauen Sie vorbei Kernel.spawn() Hier.Dies scheint das allgemeinste/umfangreichste zu sein, was von angeboten wird Lager Ruby um externe Befehle auszuführen.

Z.B.Sie können es verwenden, um:

  • Prozessgruppen erstellen (Windows)
  • Ein- und Ausleiten, Fehler in Dateien/untereinander umleiten.
  • set env vars, umask
  • Ändern Sie das Verzeichnis, bevor Sie den Befehl ausführen
  • Legen Sie Ressourcenlimits für CPU/Daten/... fest.
  • Machen Sie alles, was mit anderen Optionen in anderen Antworten möglich ist, aber mit mehr Code.

Offiziell Ruby-Dokumentation hat ausreichend gute Beispiele.

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)
  • Die Backticks-Methode ist die einfachste Methode zum Aufrufen von Shell-Befehlen aus Ruby.Es gibt das Ergebnis des Shell-Befehls zurück.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    

Wenn ein Befehl gegeben wird, z. B. attrib

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

Ich habe festgestellt, dass diese Methode zwar nicht so einprägsam ist wie z.B.system("thecommand") oder thecommand in Backticks, eine gute Sache an dieser Methode im Vergleich zu anderen Methoden.ist z.B.Mit Backticks kann ich den Befehl, den ich ausführe, nicht in eine Variable einfügen bzw. speichern, und system("thecommand") scheint mir die Ausgabe nicht zu ermöglichen.Mit dieser Methode hingegen kann ich beides tun und unabhängig voneinander auf stdin, stdout und stderr zugreifen.

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html

Keine wirkliche Antwort, aber vielleicht findet das jemand nützlich und es geht darum.

Wenn Sie die TK-GUI unter Windows verwenden und Shell-Befehle von Rubyw aus aufrufen müssen, wird für weniger als eine Sekunde immer ein lästiges Cmd-Fenster angezeigt.

Um dies zu vermeiden, können Sie verwenden

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

oder

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

Beide speichern die Ausgabe von ipconfig in „log.txt“, aber es werden keine Fenster angezeigt.

Das wirst du tun müssen require 'win32ole' in Ihrem Skript.

system(), exec() Und spawn() Bei der Verwendung von TK und Rubyw wird dieses nervige Fenster auftauchen.

Hier ist ein cooles, das ich in einem Ruby-Skript unter OS X verwende (damit ich ein Skript starten und ein Update erhalten kann, auch nachdem ich das Fenster verlassen habe):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top