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
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}`
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} ]
Kernel#system
Führt den angegebenen Befehl in einer Subshell aus.
Gibt
true
wenn der Befehl gefunden wurde und erfolgreich ausgeführt wurde, andernfallsfalse
.Dokumente: http://ruby-doc.org/core/Kernel.html#method-i-system
wasGood = system( "echo 'hi'" ) wasGood = system( cmd )
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:
- Zeichnet STDOUT und STDERR sauber auf, damit sie nicht "lecken", wenn mein Skript von der Konsole aus ausgeführt wird.
- Erlaubt die Übergabe von Argumenten an die Shell als Array, sodass Sie sich keine Gedanken über das Escaping machen müssen.
- 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}`