Wie erhalte ich die Ausgabe, wenn ich einen Befehl mit dem Kernel # -System in Ruby aufrufe?
system("ls")
Wie erhalte ich die Ausgabe, wenn ich einen Befehl mit dem Kernel # -System in Ruby aufrufe?
system("ls")
Antworten:
Ich möchte die Antwort des Chaos ein wenig erweitern und klären .
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 Zeichenfolge zurück. Sie können den Wert dann einer Variablen wie folgt zuweisen:
output = `ls`
p output
oder
printf output # escapes newline chars
ls #{filename}
.
command 2>&1
Seien Sie sich bewusst , dass alle Lösungen , bei denen Sie einen String mit Benutzer angegebenen Werte weitergeben system
, %x[]
usw. unsicher sind! Unsicher bedeutet eigentlich: Der Benutzer kann Code auslösen, der im Kontext und mit allen Berechtigungen des Programms ausgeführt wird.
Soweit ich nur sagen kann system
und Open3.popen3
eine sichere / flüchtende Variante in Ruby 1.8 bereitstelle. In Ruby 1.9 IO::popen
akzeptiert 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, das Sie wahrscheinlich verwenden möchten Open3.popen3
:
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üssten sie explizit geschlossen werden .
Weitere Informationen finden Sie hier: Erstellen von Sanitär-Shell-Befehlen oder Systemaufrufen in Ruby
gets
Aufrufe sollten das Argument übergeben nil
, da wir sonst nur die erste Zeile der Ausgabe erhalten. Also zB stdout.gets(nil)
.
Open3.popen3
ein großes Problem fehlt: Wenn Sie einen Unterprozess haben, der mehr Daten in stdout schreibt, als eine Pipe aufnehmen kann, wird der Unterprozess angehalten stderr.write
und Ihr Programm bleibt hängen stdout.gets(nil)
.
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?
output=`ls no_existing_file 2>&1`; result=$?.success?
Die einfache Möglichkeit , dies richtig zu tun und sicher zu bedienen Open3.capture2()
, Open3.capture2e()
oder Open3.capture3()
.
Die Verwendung von Rubys Backticks und seines %x
Alias ist unter keinen Umständen sicher, wenn sie mit nicht vertrauenswürdigen Daten verwendet werden. Es ist schlicht und einfach GEFÄHRLICH :
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
Im system
Gegensatz dazu entgeht die Funktion Argumenten bei korrekter Verwendung ordnungsgemäß :
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Das Problem ist, dass der Exit-Code anstelle der Ausgabe zurückgegeben wird und die Erfassung des letzteren kompliziert und chaotisch ist.
Die beste Antwort in diesem Thread erwähnt bisher Open3, aber nicht die Funktionen, die für die Aufgabe am besten geeignet sind. Open3.capture2
, capture2e
Und capture3
Arbeit wie system
, kehrt aber zwei oder drei Argumente:
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
Ein anderer erwähnt IO.popen()
. Die Syntax kann in dem Sinne ungeschickt sein, dass ein Array als Eingabe gewünscht wird, aber es funktioniert auch:
out = IO.popen(['echo', untrusted]).read # good
Der Einfachheit halber können Sie Open3.capture3()
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')
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üssten sie explizit geschlossen werden .
capture2
, capture2e
und capture3
auch sie std * s automatisch schließen. (Zumindest bin ich nie auf das Problem gestoßen.)
Open3#popen2
, popen2e
und popen3
mit einem vorgegebenen Block: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
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 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 [..] speichert andererseits 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 Blog-Beitrag von Jay Fields erklärt ausführlich die Unterschiede zwischen der Verwendung von system, exec und% x [..].
Wenn Sie den Argumenten entkommen müssen, akzeptiert IO.popen in Ruby 1.9 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 auch stdin bestehen müssen, sollte dies sowohl in 1.9 als auch in 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`
ruby -e '%x{ls}'
- beachten Sie, keine Ausgabe. (fyi %x{}
ist gleichbedeutend mit backticks.)
sh
würde die Ausgabe an die Konsole (dh STDOUT) zurückgeben und zurückgeben. Das geht nicht.
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 einzuspeisen und dessen Standardausgabe zu lesen.
Ich fand, dass Folgendes nützlich ist, wenn Sie den Rückgabewert benötigen:
result = %x[ls]
puts result
Ich wollte speziell die Pids aller Java-Prozesse auf meinem Computer auflisten und habe Folgendes verwendet:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
Wie Simon Hürlimann bereits erklärte , 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üssten sie explizit geschlossen werden .
Während die Verwendung von Backticks oder Popen oft das ist, was Sie wirklich wollen, beantwortet es die gestellte Frage nicht wirklich. Es kann gültige Gründe für die Erfassung der system
Ausgabe geben (möglicherweise für automatisierte Tests). Ein kleines Googeln ergab eine Antwort, von der ich dachte, ich würde sie hier zum Nutzen anderer veröffentlichen.
Da ich dies zum Testen benötigte, verwendet mein Beispiel ein Block-Setup, um die Standardausgabe zu erfassen, da der eigentliche system
Aufruf im zu testenden Code vergraben 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 alle Ausgaben im angegebenen Block mithilfe einer Tempfile, um die tatsächlichen Daten zu speichern. Anwendungsbeispiel:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
Sie können den system
Anruf durch alles ersetzen , was intern anruft system
. Sie können auch eine ähnliche Methode verwenden, um zu erfassen, stderr
wenn Sie möchten.
Wenn Sie möchten, dass die Ausgabe mithilfe von in eine Datei umgeleitet wird Kernel#system
, können Sie Deskriptoren wie folgt ändern:
Leiten Sie stdout und stderr im Append-Modus in eine Datei (/ tmp / log) um:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Bei einem Befehl mit langer Laufzeit wird die Ausgabe in Echtzeit gespeichert. Sie können die Ausgabe auch mit einer IO.pipe speichern und vom Kernel # -System umleiten.
Als direkten 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 hatte ich einige Probleme, die volle Ausgabe zu erhalten.
Sie können STDERR zu STDOUT umleiten, wenn Sie STDERR mit einem Backtick erfassen möchten.
output = `grep hosts / private / etc / * 2> & 1`
Quelle: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
puts `date`
puts $?
Mon Mar 7 19:01:15 PST 2016
pid 13093 exit 0