359 Stimmen

Ausgabe von system()-Aufrufen in Ruby erhalten

Wenn ich einen Befehl aufrufe mit Kernel#System in Ruby, wie erhalte ich die Ausgabe?

system("ls")

392voto

Craig Walker Punkte 46757

Ich möchte das Thema erweitern und präzisieren Die Antwort des Chaos ein wenig.

Wenn Sie Ihren Befehl mit Backticks umgeben, müssen Sie system() gar nicht (explizit) aufrufen. Die Backticks führen den Befehl aus und geben die Ausgabe als String zurück. Sie können den Wert dann wie folgt einer Variablen zuweisen:

output = `ls`
p output

o

printf output # escapes newline chars

265voto

Simon Hürlimann Punkte 4031

Beachten Sie, dass alle Lösungen, bei denen Sie eine Zeichenkette mit benutzerdefinierten Werten an system , %x[] usw. sind unsicher! Unsicher bedeutet eigentlich: Der Benutzer darf Code im Kontext und mit allen Rechten des Programms zur Ausführung bringen.

Soweit ich sagen kann, nur system y Open3.popen3 bieten in Ruby 1.8 eine Secure/Escaping-Variante. 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, sollten Sie wahrscheinlich 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 die Blockform stdin, stdout und stderr automatisch schließt - andernfalls müssten sie in ausdrücklich geschlossen .

Weitere Informationen finden Sie hier: Synthetische Shell-Befehle oder Systemaufrufe in Ruby formulieren

181voto

FernandoFabreti Punkte 1855

Nur für das Protokoll, wenn Sie beides wollen (Ausgabe und Ergebnis der Operation) können Sie tun:

output=`ls no_existing_file` ;  result=$?.success?

91voto

Denis de Bernardy Punkte 71628

Der einfachste Weg, dies korrekt und sicher zu tun, ist die Verwendung von Open3.capture2() , Open3.capture2e() , oder Open3.capture3() .

Mit den Backticks von Ruby und seiner %x Alias sind UNTER KEINEN UMSTÄNDEN SICHER 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

En system Funktion werden die Argumente dagegen korrekt umgangen bei richtiger Anwendung :

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 ist umständlich und unübersichtlich.

Die bisher beste Antwort in diesem Thread erwähnt zwar Open3, aber nicht die Funktionen, die für diese Aufgabe am besten geeignet sind. Open3.capture2 , capture2e y capture3 arbeiten wie system , gibt aber 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

Eine weitere Erwähnung IO.popen() . Die Syntax kann in dem Sinne ungeschickt sein, dass sie ein Array als Eingabe verlangt, aber sie funktioniert auch:

out = IO.popen(['echo', untrusted]).read               # good

Der Einfachheit halber können Sie Folgendes einpacken Open3.capture3() in einer Funktion, z.B.:

#
# 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 das Folgende:

nil
nil
false
false
/usr/bin/which         <— stdout from system('which', 'which')
true                   <- p system('which', 'which')
"/usr/bin/which"       <- p syscall('which', 'which')

61voto

Martin Gross Punkte 931

Sie können system() oder %x[] verwenden, je nachdem, welche Art von Ergebnis Sie benötigen.

system() liefert true, 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[..] hingegen speichert die Ergebnisse des Befehls als Zeichenkette:

>> 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

Th Blogbeitrag von Jay Fields erklärt im Detail die Unterschiede zwischen der Verwendung von system, exec und %x[ ] .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X