Shell-Befehle von Ruby aus aufrufen

Translate

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

This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Diese Erklärung basiert auf einem KommentarRuby-Skriptvon 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 beim Aufrufen einer Shell normalerweise aufruft/bin/sh, nichtBash. Einige Bash-Syntax wird von nicht unterstützt/bin/shauf allen Systemen.

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

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

    Dies ist wie bei vielen anderen Sprachen, einschließlich Bash, PHP und Perl.

    Gibt das Ergebnis (dh die Standardausgabe) des Shell-Befehls zurück.

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

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

    Nach demxZeichen 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 übereinstimmenden schließenden Trennzeichen unter Berücksichtigung verschachtelter Trennzeichenpaare. Bei allen anderen Trennzeichen umfasst das Literal die Zeichen bis zum nächsten Auftreten des Trennzeichens. String-Interpolation#{ ... }ist erlaubt.

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

    Docs: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ücktrueWenn der Befehl gefunden und erfolgreich ausgeführt wurde,falseAndernfalls.

    Docs: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.

    Docs: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 zuletzt vom System ausgeführten Befehls zu, wenn Sie die Backticks verwenden,system()oder%x{}. Sie können dann auf die zugreifenexitstatusundpidEigenschaften:

$?.exitstatus

Weitere Informationen finden Sie unter:

Quelle
Ian
Translate

Hier ist ein Flussdiagramm basierend aufdiese Antwort. Siehe auch,mitscriptein Terminal emulieren.

enter image description here

Quelle
Translate

Die Art und Weise, wie ich das mache, ist die Verwendung von%xLiteral, das es einfach (und lesbar!) macht, Anführungszeichen in einem Befehl zu verwenden, wie folgt:

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
Quelle
Translate

Hier ist meiner Meinung nach der beste Artikel 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 habe ich das Open4-Juwel verwendet. Sie haben dort alle Methoden erklärt.

Quelle
Translate

Mein Favorit istOpen3

  require "open3"

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

Einige Dinge, über die Sie nachdenken sollten, wenn Sie zwischen diesen Mechanismen wählen, sind:

  1. Willst du nur stdout oder brauchst du auch stderr? oder sogar getrennt?
  2. Wie groß ist Ihre Leistung? Möchten Sie das gesamte Ergebnis im Speicher behalten?
  3. Möchten Sie einen Teil Ihrer Ausgabe lesen, während der Unterprozess noch ausgeführt wird?
  4. Benötigen Sie Ergebniscodes?
  5. Benötigen Sie ein Ruby-Objekt, das den Prozess darstellt und es Ihnen ermöglicht, ihn bei Bedarf zu beenden?

Möglicherweise benötigen Sie einfache Backticks (``), system () undIO.popenzu ausgewachsenKernel.fork/Kernel.execmitIO.pipeundIO.select.

Möglicherweise möchten Sie auch Timeouts in den Mix einfügen, wenn die Ausführung eines Unterprozesses zu lange dauert.

Leider sehr vielhängt davon ab.

Quelle
Translate

Noch eine Option:

Wenn du:

  • brauche sowohl stderr als auch stdout
  • kann / wird Open3 / Open4 nicht verwenden (sie werfen 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"

Das2>&1Syntax funktioniert überLinux, Mac undWindowsseit den Anfängen von MS-DOS.

Quelle
Translate

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)
Quelle
Translate

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

Grundsätzlich sagt es uns:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemund$?:

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

Wenn Sie Bash wirklich brauchen, lesen Sie den Hinweis in der "besten" Antwort.

Beachten Sie zunächst, dass Ruby beim Aufrufen einer Shell normalerweise aufruft/bin/sh, nichtBash. Einige Bash-Syntax wird von nicht unterstützt/bin/shauf allen Systemen.

Wenn Sie Bash verwenden müssen, fügen Sie einbash -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 (zscript_output = system("./my_script.sh")) RubysollteEhre den Schebang, aber du könntest ihn immer gebrauchensystem("bash ./my_script.sh")um sicher zu gehen (obwohl es einen leichten Overhead von geben kann/bin/shLaufen/bin/bashSie werden es wahrscheinlich nicht bemerken.

Quelle
Translate

Sie können auch die Backtick-Operatoren (`) verwenden, ähnlich wie bei 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 möchten. Weitere Informationen zu den verschiedenen Methoden finden Sie in den Dokumenten.

Quelle
nkm
Translate

Der einfachste Weg ist zum Beispiel:

reboot = `init 6`
puts reboot
Quelle
Translate

Mit den Antworten hier und in Mihais Antwort verknüpft, habe ich eine Funktion zusammengestellt, die diese Anforderungen erfüllt:

  1. Erfasst STDOUT und STDERR ordentlich, damit sie nicht "auslaufen", wenn mein Skript von der Konsole ausgeführt wird.
  2. Ermöglicht die Übergabe von Argumenten an die Shell als Array, sodass Sie sich keine Gedanken über das Escape machen müssen.
  3. Erfasst den Beendigungsstatus 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 STDOUT aktiviert. Auf diese Weise unterscheidet es sich vonsystem, die einfach zurückkehrttruein solchen Fällen.

Code folgt. Die spezifische Funktion istsystem_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"
Quelle
Translate

Wir können es auf verschiedene Arten erreichen.

Verwenden vonKernel#exec, nichts nachdem dieser Befehl ausgeführt wurde:

exec('ls ~')

Verwenden vonbackticks or %x

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

Verwenden vonKernel#systemBefehl, kehrt zurücktrueFalls erfolgreich,falsewenn nicht erfolgreich und kehrt zurücknilWenn die Befehlsausführung fehlschlägt:

system('ls ~')
=> true
Quelle
Translate

Vergiss das nichtspawnBefehl zum Erstellen eines Hintergrundprozesses zum Ausführen des angegebenen Befehls. Sie können sogar mit dem warten, bis der Vorgang abgeschlossen istProcessKlasse und die zurückgegebenpid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

Das Dokument sagt: Diese Methode ähnelt#systemEs wartet jedoch nicht darauf, dass der Befehl beendet wird.

Quelle
Translate

Wenn Sie einen komplexeren Fall als den allgemeinen haben (der nicht behandelt werden kann)``) dann auscheckenKernel.spawn() Hier. Dies scheint das allgemeinste / umfassendste Angebot von zu seinstock Rubyexterne Befehle ausführen.

ZB können Sie es verwenden, um:

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

OffiziellRuby-Dokumentationhat gut genug 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)
Quelle
ysk
Translate
  • Die Methode backticks `ist die einfachste Methode, um Shell-Befehle von Ruby aus aufzurufen. Es gibt das Ergebnis des Shell-Befehls zurück.

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

Gegeben ein Befehl zB 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. das System ("thecommand") oder der Befehl in Backticks, aber eine gute Sache bei dieser Methode im Vergleich zu anderen Methoden ist, dass z. B. Backticks mich nicht zuzulassen scheinen 'Der Befehl, den ich ausführe / speichere, der Befehl, den ich ausführen möchte, in einer Variablen, und das System ("thecommand") scheint mich nicht die Ausgabe abrufen zu lassen. Mit dieser Methode 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

Quelle
Translate

Keine wirkliche Antwort, aber vielleicht findet jemand dies nützlich und es betrifft dies.

Wenn Sie die TK-GUI unter Windows verwenden und Shell-Befehle von rubyw aus aufrufen müssen, wird immer ein nerviges cmd-Fenster für weniger als eine Sekunde 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', es werden jedoch keine Fenster geöffnet.

Du wirst es brauchenrequire 'win32ole'in Ihrem Skript.

system(), exec()undspawn()Wenn Sie TK und Rubyw verwenden, wird dieses nervige Fenster geöffnet.

Quelle
Translate

Hier ist eine coole, die ich in einem Ruby-Skript unter OS X verwende (damit ich ein Skript starten und ein Update erhalten kann, auch nachdem ich aus dem Fenster gewechselt habe):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Quelle