rails - Aufruf von Shell-Befehlen von Ruby




ruby use shell (13)

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


Der einfachste Weg ist zum Beispiel:

reboot = `init 6`
puts reboot

Die Art, wie ich das tue, ist die Verwendung des %x Literals, 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

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 unter dem Link aktualisieren.

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

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 )

    Dem x Zeichen folgt ein Trennzeichen, das ein beliebiges Zeichen sein kann. Wenn das Trennzeichen eines der Zeichen ( , [ , { oder < , besteht das Literal aus den Zeichen bis zum entsprechenden schließenden Trennzeichen, wobei verschachtelte Trennzeichenpaare berücksichtigt werden. Bei allen anderen Trennzeichen umfasst das Literal die Zeichen bis zum nächstes Auftreten des Trennzeichens Zeichenfolgeninterpolation #{ ... } 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.

    Gibt true wenn der Befehl gefunden wurde und erfolgreich ausgeführt wurde, andernfalls false .

    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 ein paar zusätzliche Ratschläge: $? , das mit $CHILD_STATUS , greift auf den Status des letzten vom System ausgeführten Befehls zu, wenn Sie die backticks, system() oder %x{} . Sie können dann auf die Eigenschaften exitstatus und pid zugreifen:

$?.exitstatus

Für mehr lesen siehe:


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"

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


Gegeben einen 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 zB System ("thecommand") oder der Befehl in Backticks, aber eine gute Sache über diese Methode im Vergleich zu anderen Methoden ist zB Backticks lässt mich nicht 'puts' 'der Befehl Ich führe / speichern Sie den Befehl, den ich in einer Variablen ausführen möchte, und System ("thecommand") scheint mir nicht die Ausgabe zu bekommen. 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


Hier ist der beste Artikel meiner Meinung nach zum Ausführen von Shell-Skripten in Ruby: " http://tech.natemurray.com/2007/03/ruby-shell-commands.html ".

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.


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

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

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)

Sie können auch die Backtick-Operatoren (`) ähnlich Perl verwenden:

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 genau Sie erreichen möchten. Weitere Informationen zu den verschiedenen Methoden finden Sie in den Dokumenten.


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

  1. Zeichnet STDOUT und STDERR sauber auf, damit sie nicht "lecken", wenn mein Skript von der Konsole aus ausgeführt wird.
  2. Erlaubt die Übergabe von Argumenten an die Shell als Array, sodass Sie sich keine Gedanken über das Escaping machen müssen.
  3. Erfasst den Beendigungsstatus des Befehls, so dass es klar ist, wenn ein Fehler aufgetreten ist.

Als Bonus gibt dieser auch STDOUT in Fällen zurück, in denen der Shell-Befehl erfolgreich beendet wird (0) und alles auf STDOUT setzt. Auf diese Weise unterscheidet es sich vom system , das in solchen Fällen einfach true zurückgibt.

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"

Wenn Sie einen komplexeren Fall als den allgemeinen Fall haben (der nicht mit `` ), dann Kernel.spawn() here . Dies scheint das allgemeinste / vollständigste Feature zu sein, das von Ruby zur Verfügung gestellt wird, um externe Befehle auszuführen.

ZB können Sie es verwenden, um:

  • Prozessgruppen anlegen (Windows)
  • Umleitung in, out, Fehler in Dateien / einander.
  • set env vars, umask
  • ändere das Verzeichnis vor dem Ausführen des Befehls
  • Setzen Sie Ressourcenlimits für CPU / Daten / ...
  • Tun Sie alles, was mit anderen Optionen in anderen Antworten möglich ist, aber mit mehr Code.

Offizielle here hat 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)

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

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

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

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 existierende Skriptdatei script_output = system("./my_script.sh") (zB script_output = system("./my_script.sh") ) Ruby sollte den Shebang beachten , aber Sie können immer system("bash ./my_script.sh") um sicher zu gehen ( obwohl es einen kleinen Overhead von /bin/sh running /bin/bash , werden Sie wahrscheinlich nicht bemerken.


  • Die Methode "backticks" ist die einfachste Shell-Anweisung von Ruby. Es gibt das Ergebnis des Shell-Befehls zurück.

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






interop