Wenn ich einen Befehl mit Kernel # system in Ruby aufrufe, wie erhalte ich seine Ausgabe?
system("ls")
Ich möchte gerne erweitern/klären die Antwort von chaos ein bisschen.
Wenn Sie Ihren Befehl mit Backticks umgeben, müssen Sie system () überhaupt nicht (explizit) aufrufen. Die Backticks führen den Befehl aus und geben die Ausgabe als String zurück. Sie können den Wert dann einer Variablen wie folgt zuordnen:
output = `ls`
p output
oder
printf output # escapes newline chars
Beachten Sie, dass alle Lösungen, bei denen Sie eine Zeichenfolge mit benutzerdefinierten Werten an system
, %x[]
usw. übergeben, unsicher sind! Unsicher bedeutet eigentlich: Der Benutzer kann auslösen, dass Code im Kontext und mit allen Berechtigungen des Programms ausgeführt wird.
Soweit ich sagen kann, bieten nur system
und Open3.popen3
eine sichere/abweichende Variante in Ruby 1.8. In Ruby 1.9 akzeptiert IO::popen
auch ein Array.
Übergeben Sie einfach jede Option und jedes Argument als Array an einen dieser Aufrufe.
Wenn Sie nicht nur den Exit-Status, sondern auch das Ergebnis benötigen, möchten Sie wahrscheinlich Open3.popen3
verwenden:
require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value
Beachten Sie, dass das Blockformular stdin, stdout und stderr automatisch schließt. Andernfalls müssen sie explizit close sein.
Weitere Informationen hier: Bilden von hygienischen Shellbefehlen oder Systemaufrufen in Ruby
Nur für die Aufzeichnung, wenn Sie beide (Ausgabe- und Operationsergebnis) möchten, können Sie Folgendes tun:
output=`ls no_existing_file` ; result=$?.success?
Der einfachste Weg, dies richtig und sicher auszuführen, ist die Verwendung von Open3.capture2()
, Open3.capture2e()
oder Open3.capture3()
.
Rubys Backticks und sein %x
-Alias sind NOT SECURE UNTER JEDEN UMSTÄNDEN, wenn sie mit nicht vertrauenswürdigen Daten verwendet werden. Es ist GEFÄHRLICH, schlicht und einfach:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
Die system
-Funktion entgeht dagegen Argumenten korrekt bei korrekter Verwendung:
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Das Problem ist, es gibt den Exit-Code anstelle der Ausgabe zurück, und das Erfassen der letzteren ist gefaltet und unordentlich.
Die bisher beste Antwort in diesem Thread erwähnt Open3, jedoch nicht die Funktionen, die für die Aufgabe am besten geeignet sind. Open3.capture2
, capture2e
und capture3
funktionieren wie system
, geben jedoch zwei oder drei Argumente zurück:
out, err, st = Open3.capture3("echo #{untrusted}") # BAD
out, err, st = Open3.capture3('echo', untrusted) # good
out_err, st = Open3.capture2e('echo', untrusted) # good
out, st = Open3.capture2('echo', untrusted) # good
p st.exitstatus
Weitere Erwähnungen IO.popen()
. Die Syntax kann in dem Sinne unbeholfen sein, dass ein Array als Eingabe gewünscht wird, es funktioniert jedoch auch:
out = IO.popen(['echo', untrusted]).read # good
Der Einfachheit halber können Sie Open3.capture3()
in eine Funktion einschließen, z.
#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
begin
stdout, stderr, status = Open3.capture3(*cmd)
status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
rescue
end
end
Beispiel:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
Ergibt folgendes:
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')
Sie können system () oder% x [] verwenden, je nachdem, welche Art von Ergebnis Sie benötigen.
system () gibt true zurück, wenn der Befehl gefunden wurde und erfolgreich ausgeführt wurde, andernfalls false.
>> s = system 'uptime'
10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status
% x [..] dagegen speichert die Ergebnisse des Befehls als Zeichenfolge:
>> result = %x[uptime]
=> "13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> p result
"13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> result.class
=> String
Der Blogbeitrag von Jay Fields erläutert detailliert die Unterschiede zwischen System, Exec und% x [..].
Ein anderer Weg ist:
f = open("|ls")
foo = f.read()
Beachten Sie, dass dies das Pipe-Zeichen vor "ls" in open ist. Dies kann auch verwendet werden, um Daten in die Standardeingabe des Programms einzugeben und deren Standardausgabe zu lesen.
Wenn Sie die Argumente ersetzen müssen, akzeptiert Ruby 1.9 IO.popen auch ein Array:
p IO.popen(["echo", "it's escaped"]).read
In früheren Versionen können Sie Open3.popen3 verwenden:
require "open3"
Open3.popen3("echo", "it's escaped") { |i, o| p o.read }
Wenn Sie stdin auch übergeben müssen, sollte dies in 1.9 und 1.8 funktionieren:
out = IO.popen("xxd -p", "r+") { |io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
Sie verwenden Backticks:
`ls`
Ich habe festgestellt, dass Folgendes hilfreich ist, wenn Sie den Rückgabewert benötigen:
result = %x[ls]
puts result
Ich wollte speziell die Pids aller Java-Prozesse auf meinem Rechner auflisten und habe folgendes verwendet:
ids = %x[ps ax | grep Java | awk '{ print $1 }' | xargs]
Wie Simon Hürlimann bereits erklärt hat, ist Open3 sicherer als Backticks usw.
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Beachten Sie, dass das Blockformular stdin, stdout und stderr automatisch schließt. Andernfalls müssen sie explizit close sein.
Wenn Sie möchten, dass die Ausgabe mithilfe von Kernel#system
in eine Datei umgeleitet wird, können Sie die Deskriptoren wie folgt ändern:
umleiten von stdout und stderr zu einer Datei (/ tmp/log) im Anfügemodus:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Bei einem langen Befehl wird die Ausgabe in Echtzeit gespeichert. Sie können die Ausgabe auch mit einer IO.pipe speichern und vom Kernel-System umleiten.
Die Verwendung von Backticks oder Popens ist zwar oft das, was Sie wirklich wollen, es beantwortet jedoch nicht die gestellte Frage. Es kann stichhaltige Gründe für die Erfassung der system
-Ausgabe geben (möglicherweise für automatisiertes Testen). Ein bisschen googeln gab eine Antwort auf Ich dachte, ich würde hier zum Wohle anderer posten.
Da ich dies zum Testen brauchte, verwendet mein Beispiel eine Blockeinstellung, um die Standardausgabe zu erfassen, da der tatsächliche Aufruf system
im getesteten Code enthalten ist:
require 'tempfile'
def capture_stdout
stdout = $stdout.dup
Tempfile.open 'stdout-redirect' do |temp|
$stdout.reopen temp.path, 'w+'
yield if block_given?
$stdout.reopen stdout
temp.read
end
end
Diese Methode erfasst jede Ausgabe in einem gegebenen Block unter Verwendung einer Servicedatei, um die tatsächlichen Daten zu speichern. Verwendungsbeispiel:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
Sie können den Aufruf system
durch alles ersetzen, das intern system
aufruft. Sie können auch eine ähnliche Methode zum Erfassen von stderr
verwenden, wenn Sie möchten.
Als direkter Systemersatz (...) können Sie Open3.popen3 (...) verwenden.
Weitere Diskussion: http://tech.natemurray.com/2007/03/Ruby-Shell-commands.html
Ich habe dieses hier nicht gefunden, also habe ich beim Hinzufügen einige Probleme mit dem vollen Output.
Sie können STDERR nach STDOUT umleiten, wenn Sie STDERR mit .__ erfassen möchten. Backtick.
output = `grep hosts/private/etc/* 2> & 1`
quelle: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-Ruby.html