Wie kann ich Shell-Befehle aus einem Ruby-Programm heraus aufrufen? Wie bekomme ich dann die Ausgabe dieser Befehle zurück in Ruby?
Antworten
Zu viele Anzeigen?Wenn Sie einen komplexeren als den üblichen Fall haben, der nicht mit ``
dann schauen Sie unter Kernel.spawn()
. Dies scheint die allgemeinste/vollste Funktion zu sein, die Ruby zur Ausführung externer Befehle bietet.
Sie können es verwenden, um:
- Prozessgruppen erstellen (Windows).
- in, out, error auf Dateien/andere umleiten.
- env vars, umask setzen.
- vor dem Ausführen eines Befehls das Verzeichnis wechseln.
- Ressourcenlimits für CPU/Daten/etc. festlegen.
- Machen Sie alles, was Sie mit anderen Optionen in anderen Antworten machen können, aber mit mehr Code.
El Ruby-Dokumentation hat genügend 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)
Mit einem Befehl wie 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
system("thecommand")
o
`thecommand`
in Backticks, ein Vorteil dieser Methode im Vergleich zu anderen Methoden ist Backticks scheinen nicht zuzulassen, dass ich puts
den Befehl, den ich ausführe, bzw. den Befehl, den ich ausführen möchte, in einer Variablen zu speichern, und system("thecommand")
scheint mir nicht zu erlauben, die Ausgabe zu erhalten, während ich mit dieser Methode beides tun kann, und sie erlaubt mir den Zugriff auf stdin, stdout und stderr unabhängig.
Siehe " Ausführen von Befehlen in Ruby " und Die Open3-Dokumentation von Ruby .
Dies ist keine wirkliche Antwort, aber vielleicht findet sie jemand nützlich:
Wenn Sie TK GUI unter Windows verwenden und Shell-Befehle von rubyw aus aufrufen müssen, erscheint immer ein lästiges CMD-Fenster, das weniger als eine Sekunde lang auftaucht.
Um dies zu vermeiden, können Sie verwenden:
WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)
o
WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)
Beide speichern die ipconfig
Ausgang innen log.txt
aber es wird kein Windows angezeigt.
Sie müssen require 'win32ole'
innerhalb Ihres Skripts.
system()
, exec()
y spawn()
werden bei der Verwendung von TK und rubyw alle dieses lästige Fenster öffnen.
Bei Shell-Befehlen bin ich mir nicht sicher. Ich habe folgendes verwendet, um die Ausgabe von Systembefehlen in eine Variable zu speichern val :
val = capture(:stdout) do
system("pwd")
end
puts val
gekürzte Version:
val = capture(:stdout) { system("pwd") }
erfassen Methode wird bereitgestellt von active_support/core_ext/kernel/reporting.rb
In ähnlicher Weise können wir auch Standardfehler erfassen mit :stderr
4 Stimmen
Diese Frage ist zwar nützlich, aber sie wird nicht gut gestellt. Ruby hat viele Möglichkeiten, Sub-Shells aufzurufen, die gut dokumentiert sind und leicht durch Lesen der Kernel y Öffnen3 Dokumentation und Suche hier auf SO.
3 Stimmen
Leider ist dieses Thema sehr komplex.
Open3
( docs ) ist die beste Wahl für die meisten Situationen, IMO, aber auf älteren Versionen von Ruby, wird es nicht respektieren eine modifiziertePATH
( bugs.ruby-lang.org/issues/8004 ), und je nachdem, wie Sie args übergeben (insbesondere, wenn Sie opts hash mit Nicht-Schlüsselwörtern verwenden), kann es zu einem Bruch kommen. Aber, wenn Sie diese Situationen treffen, dann tun Sie etwas ziemlich fortgeschritten und Sie können herausfinden, was zu tun ist, indem Sie die Implementierung vonOpen3
.6 Stimmen
Ich bin überrascht, dass niemand erwähnt hat
Shellwords.escape
( doc ). Sie möchten keine Benutzereingaben direkt in Shell-Befehle einfügen - geben Sie sie zuerst in einer Escape-Funktion ein! Siehe auch Befehlsinjektion .