Aufruf-Shell-Befehle von Ruby

stimmen
862

Wie rufe ich Shell-Befehle aus dem Inneren eines Ruby-Programm? Wie kann ich dann von diesen Befehlen zurück in Rubin bekommen?

Veröffentlicht am 05/08/2008 um 13:56
quelle vom benutzer
In anderen Sprachen...                            


20 antworten

stimmen
1k

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 zu aktualisieren.

Erstens ist zu beachten , dass , wenn Ruby eine Shell ruft, es in der Regel ruft /bin/sh, nicht Bash. Einige Bash Syntax wird nicht unterstützt durch /bin/shauf allen Systemen.

Hier sind Möglichkeiten, um einen 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-Befehl.

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

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

    Nach dem xCharakter ist ein Begrenzer, der ein beliebiges Zeichen sein kann. Wenn das Trennzeichen eines der Zeichen ist (, [, {, oder <besteht die wörtliche der Zeichen bis zum passenden Endbegrenzer Berücksichtigung der verschachtelten delimiter Paare nehmen. Für alle anderen Begrenzer umfasst das Literal die Zeichen bis zum nächsten Auftreten des Begrenzungszeichens. String Interpolation #{ ... }erlaubt.

    Gibt das Ergebnis des Shell-Befehl, 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.

    Gibt truewenn der Befehl gefunden wurde und lief erfolgreich, falseansonsten.

    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 den gegebenen externen Befehl ausgeführt wird.

    Gibt keine, wird der aktuelle Prozess ersetzt und nie weiter.

    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 Beratung: $?, das ist die gleiche wie $CHILD_STATUSgreift der Status des letzten Systembefehl ausgeführt , wenn Sie die Backticks verwenden, system()oder %x{}. Sie können dann den Zugriff exitstatusund pidEigenschaften:

$?.exitstatus

Für weitere Lesung zu finden unter:

Beantwortet am 05/08/2008 um 15:42
quelle vom benutzer

stimmen
150

Die Art und Weise Ich mag , dies zu tun ist , um die Verwendung von %xwörtlichen, was es einfach macht , zitiert in einem Befehl zu verwenden, wie so (und lesbar!):

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

Welche, in diesem Fall wird die Dateiliste mit allen Testdateien im aktuellen Verzeichnis füllen, die Sie als erwartet verarbeiten können:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
Beantwortet am 05/08/2008 um 15:08
quelle vom benutzer

stimmen
140

Hier ist ein Flussdiagramm auf der Grundlage dieser Antwort . Siehe auch, indem scriptein Terminal zu emulieren .

Geben Sie hier image description

Beantwortet am 19/05/2016 um 17:01
quelle vom benutzer

stimmen
58

Hier ist der beste Artikel meiner Meinung nach über Shell - Skripte in Ruby ausgeführt wird : „ 6 Wege zu starten Shell - Befehle in Ruby “.

Wenn Sie nur den Ausgang Verwendung Backticks zu erhalten.

Ich brauchte fortgeschrittenere Sachen wie STDOUT und STDERR, damit ich das Open4 Juwel verwendet. Sie haben alle Methoden dort erläutert.

Beantwortet am 02/09/2008 um 12:05
quelle vom benutzer

stimmen
31

Mein Favorit ist Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Beantwortet am 18/09/2008 um 18:47
quelle vom benutzer

stimmen
23

Einige Dinge zu denken, wenn zwischen diesen Mechanismen der Wahl sind:

  1. Wollen Sie nur stdout oder haben Sie stderr als auch brauchen? oder sogar abgetrennt?
  2. Wie groß ist Ihr Ausgang? Wollen Sie das gesamte Ergebnis im Speicher halten?
  3. Wollen Sie einige Ihrer Ausgabe lesen, während der Unterprozess noch läuft?
  4. Sie benötigen Ergebnis-Codes?
  5. Sie benötigen ein Ruby-Objekt, das den Prozess darstellt, und können Sie es auf Nachfrage töten?

Sie können alles von einfachen Backticks ( ``), system () benötigen, und IO.popenzu ausgewachsenen Kernel.fork/ Kernel.execmit IO.pipeund IO.select.

Vielleicht möchten Sie auch Timeouts in die Waagschale werfen, wenn ein Teilprozess zu lange dauert auszuführen.

Leider ist es sehr viel abhängt .

Beantwortet am 07/08/2008 um 06:10
quelle vom benutzer

stimmen
19

Eine weitere Option:

Wenn du:

  • müssen stderr sowie stdout
  • kann nicht / nicht Open3 / Open4 verwenden (sie werfen Ausnahmen in NetBeans auf meinem Mac, keine Ahnung warum)

Sie können 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>&1Syntax funktioniert über Linux , Mac und Windows - seit den frühen Tagen von MS-DOS.

Beantwortet am 16/06/2010 um 03:13
quelle vom benutzer

stimmen
15

Ich bin definitiv kein Experte Rubin, aber ich gebe ihm einen Schuss:

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)
Beantwortet am 05/08/2008 um 14:24
quelle vom benutzer

stimmen
11

Die Antworten oben sind schon recht groß, aber ich mag die folgende Zusammenfassung Artikel teilen: „ 6 Wege Shell - Befehle in Ruby starten

Im Grunde ist es sagt 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>]
Beantwortet am 07/06/2013 um 03:07
quelle vom benutzer

stimmen
7

Wenn Sie wirklich brauchen, Bash, pro die Notiz in der „beste“ Antwort.

Erstens ist zu beachten , dass , wenn Ruby eine Shell ruft, es in der Regel ruft /bin/sh, nicht Bash. Einige Bash Syntax wird nicht unterstützt durch /bin/shauf allen Systemen.

Wenn Sie Bash verwenden müssen, legen Sie bash -c "your Bash-only command"innerhalb des gewünschten Aufruf der Methode.

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 ausgeführt werden (zB script_output = system("./my_script.sh")) Rubin sollte die shebang ehren, aber man konnte immer verwenden , system("bash ./my_script.sh")um sicherzustellen , (obwohl es ein leichter Kopf aus sein kann /bin/shlaufen /bin/bash, werden Sie wahrscheinlich nicht bemerken.

Beantwortet am 02/06/2017 um 20:14
quelle vom benutzer

stimmen
7

Sie können auch die Graviszeichen Operatoren ( `), ähnlich wie Perl verwenden:

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

Praktisch, wenn Sie brauchen etwas einfach.

Welche Methode Sie verwenden möchten, hängt davon ab, genau das, was Sie erreichen wollen; Überprüfen Sie die Dokumentation, um weitere Informationen über die verschiedenen Methoden.

Beantwortet am 05/08/2008 um 14:57
quelle vom benutzer

stimmen
5

Vergessen , die nicht spawnBefehl einen Hintergrundprozess zu erstellen , den Befehl auszuführen. Sie können sogar für seine Fertigstellung warten , um die Verwendung von ProcessKlasse und der zurück 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

Die Doc sagt: Diese Methode ist ähnlich #systemaber nicht für den Befehl zu beenden warten.

Beantwortet am 04/11/2015 um 15:04
quelle vom benutzer

stimmen
5

Mit den Antworten hier und in Mihai Antwort verknüpft ist, habe ich eine Funktion zusammen, die diese Anforderungen erfüllen:

  1. Ordentlich fängt STDOUT und STDERR, so dass sie nicht „Leck“, wenn mein Skript von der Konsole ausgeführt wird.
  2. Ermöglicht Argumente an die Shell als Array übergeben werden, so dass es keinen Grund zur Sorge über zu entkommen.
  3. Erfasst den Exit-Status des Befehls so klar ist, wenn ein Fehler aufgetreten ist.

Als Bonus wird auch dieser STDOUT in Fällen zurück , wo die Shell - Befehl beendet erfolgreich (0) und alles auf STDOUT setzt. Auf diese Weise unterscheidet sich von system, die einfach wieder truein 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"
Beantwortet am 21/02/2012 um 00:36
quelle vom benutzer

stimmen
5

Wir können es auf vielfältige Weise erreicht werden.

Mit Kernel#execnichts nach diesem Befehl ausgeführt wird:

exec('ls ~')

Mit backticks or %x

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

Mit Kernel#systemBefehl, kehrt , truewenn erfolgreich, falsewenn nicht erfolgreich und kehrt , nilwenn die Befehlsausführung fehlschlägt:

system('ls ~')
=> true
Beantwortet am 19/02/2012 um 19:07
quelle vom benutzer

stimmen
4

einfachste Weg ist, zum Beispiel:

reboot = `init 6`
puts reboot
Beantwortet am 30/03/2017 um 18:13
quelle vom benutzer

stimmen
3
  • Backticks `Verfahren ist das einfachste von Ruby Shell-Befehle zu nennen. Es gibt das Ergebnis des Shell-Befehl.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
Beantwortet am 16/02/2017 um 09:58
quelle vom benutzer

stimmen
3

Wenn Sie einen komplexeren Fall als die gemeinsame Fall haben (also nicht mit behandelt werden können ``) dann schauen Sie Kernel.spawn() hier . Dies scheint die generic / full-featured , bereitgestellt durch sein Lager Rubin externe Befehle auszuführen.

Zum Beispiel können Sie es verwenden:

  • erstellen Prozessgruppen (Windows)
  • Umleitung in, out, Fehler zu Dateien / Each andere.
  • set env vars, umask
  • ändern DIR bevor Befehl ausführt
  • Satz Ressourcengrenzen für CPU / data / ...
  • Tun Sie alles, die mit anderen Optionen in anderen Antworten getan werden können, aber mit mehr Code.

Offizielle Rubin Dokumentation hat 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)
Beantwortet am 11/12/2015 um 14:57
quelle vom benutzer

stimmen
1

Bei einem Befehl zB attrib

require 'open3'

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

Ich habe festgestellt, dass, während diese Methode wie zB System ( „thecommand“) oder thecommand in Backticks nicht so angenehm ist, eine gute Sache über diese Methode zu anderen Methoden verglichen .. ist zB Backticks nicht zu lassen, scheinen mich ‚puts 'der Befehl I / Laden laufe den Befehl I in einer variablen ausgeführt werden soll, und das System ( ‚thecommand‘) scheint nicht lassen Sie mir die Ausgabe erhalten. Diese Art mir diese beiden Dinge können tun, und es lässt mich Zugang stdin, stdout und stderr unabhängig.

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

Beantwortet am 19/12/2017 um 05:54
quelle vom benutzer

stimmen
0

Nicht wirklich eine Antwort, aber vielleicht jemand findet diese nützlich, und sein in Bezug auf diese.

Wenn TK GUI unter Windows verwenden und u müssen Shell-Befehle von rubyw nennen, wird u immer ein lästiges cmd Fenster dann einer Sekunde für weniger Aufspringen haben.

Um dies zu vermeiden kann u verwenden

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

oder

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

Beide werden ipconfig der Ausgang innen ‚log.txt‘ speichern, aber keine Fenster wird kommen.

U müssen require 'win32ole'in Ihrem Skript.

system(), exec()Und spawn()wird die lästigen Pop - up - Fenster alle , wenn TK und rubyw verwenden.

Beantwortet am 05/07/2018 um 12:55
quelle vom benutzer

stimmen
-1

Hier ist ein cooler, die ich in einem Ruby-Skript auf OS X verwenden (so dass ich ein Skript starten und erhalten ein Update auch nach Makeln weg vom Fenster):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Beantwortet am 14/10/2014 um 21:12
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more