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:
Was ist, wenn ich als Teil meines Befehls eine Variable angeben muss? Das heißt, was würde so etwas wie ein System ("ls" + Dateiname) bedeuten, wenn Backticks verwendet werden sollen?
Vijay Dev
47
Sie können die Ausdrucksauswertung wie bei normalen Zeichenfolgen durchführen : ls #{filename}.
Craig Walker
36
Diese Antwort ist nicht ratsam: Sie führt das neue Problem der nicht bereinigten Benutzereingaben ein.
Dogweather
4
@ Dogweather: Das mag wahr sein, aber unterscheidet es sich von den anderen Methoden?
Craig Walker
20
Wenn Sie stderr erfassen möchten, setzen Sie einfach 2> & 1 am Ende Ihres Befehls. zB Ausgabe =command 2>&1
mikred
243
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 systemund Open3.popen3eine sichere / flüchtende Variante in Ruby 1.8 bereitstelle. In Ruby 1.9 IO::popenakzeptiert 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:
Dies ist die einzige Antwort, die die Frage tatsächlich beantwortet und das Problem löst, ohne neue einzuführen (nicht bereinigte Eingabe).
Dogweather
2
Vielen Dank! Dies ist die Art von Antwort, auf die ich gehofft hatte. Eine Korrektur: Die getsAufrufe sollten das Argument übergeben nil, da wir sonst nur die erste Zeile der Ausgabe erhalten. Also zB stdout.gets(nil).
Weiß jemand, ob sich in Ruby 2.0 oder 2.1 etwas geändert hat? Änderungen oder Kommentare sind willkommen ;-)
Simon Hürlimann
1
Ich denke, dass in der Diskussion Open3.popen3ein 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.writeund Ihr Programm bleibt hängen stdout.gets(nil).
Hagello
165
Nur für die Aufzeichnung, wenn Sie beide (Ausgabe und Operationsergebnis) möchten, können Sie Folgendes tun:
Das erfasst nur stdout und stderr geht zur Konsole. Um stderr zu bekommen, verwenden Sie: output=`ls no_existing_file 2>&1`; result=$?.success?
Peterept
8
Diese Antwort ist unsicher und sollte nicht verwendet werden. Wenn der Befehl alles andere als eine Konstante ist, verursacht die Backtick-Syntax wahrscheinlich einen Fehler, möglicherweise eine Sicherheitslücke. (Und selbst wenn es sich um eine Konstante handelt, wird sie wahrscheinlich dazu führen, dass jemand sie später für eine Nichtkonstante verwendet und einen Fehler verursacht.) Eine korrekte Lösung finden Sie in der Antwort von Simon Hürlimann .
Greg Price
23
Ein großes Lob an Greg Price für das Verständnis der Notwendigkeit, Benutzereingaben zu entgehen, aber es ist nicht richtig zu sagen, dass diese Antwort in der geschriebenen Form unsicher ist. Die erwähnte Open3-Methode ist komplizierter und führt zu mehr Abhängigkeiten, und das Argument, dass jemand "sie später für eine Nichtkonstante verwenden wird", ist ein Strohmann. Zwar würden Sie sie wahrscheinlich nicht in einer Rails-App verwenden, aber für ein einfaches Systemdienstprogramm-Skript ohne die Möglichkeit nicht vertrauenswürdiger Benutzereingaben sind Backticks vollkommen in Ordnung, und niemand sollte sich schlecht fühlen, wenn er sie verwendet.
Die Verwendung von Rubys Backticks und seines %xAlias 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 systemGegensatz 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, capture2eUnd capture3Arbeit 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 eolrescueendend
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:
nilnilfalsefalse/usr/bin/which <— stdout from system('which','which')true<- p system('which','which')"/usr/bin/which"<- p syscall('which','which')
Dies ist die richtige Antwort. Es ist auch das informativste. Das einzige, was fehlt, ist eine Warnung vor dem Schließen der Standards. Siehe diesen anderen Kommentar : 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 .
Peter H. Boling
@ PeterH.Boling: Am besten ist mir bewusst, dass capture2, capture2eund capture3auch sie std * s automatisch schließen. (Zumindest bin ich nie auf das Problem gestoßen.)
Denis de Bernardy
Ohne die Verwendung des Blockformulars kann eine Codebasis nicht wissen, wann etwas geschlossen werden sollte, daher bezweifle ich sehr, dass sie geschlossen werden. Sie sind wahrscheinlich nie auf ein Problem gestoßen, weil das Nicht-Schließen nicht zu Problemen in einem kurzlebigen Prozess führt. Wenn Sie einen lang laufenden Prozess häufig genug neu starten, wird otto dort auch nicht angezeigt, es sei denn, Sie öffnen std * s in eine Schleife. Linux hat ein hohes Dateideskriptor-Limit, das Sie treffen können, aber bis Sie es treffen, werden Sie den "Fehler" nicht sehen.
Peter H. Boling
2
@ PeterH.Boling: Nein, nein, siehe Quellcode. Die Funktionen sind nur Wrapper um Open3#popen2, popen2eund popen3mit einem vorgegebenen Block: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
Denis de Bernardy
1
@Dennis de Barnardy Vielleicht haben Sie verpasst, dass ich auf dieselbe Klassendokumentation verlinkt habe (allerdings für Ruby 2.0.0 und eine andere Methode. Ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/… Aus dem Beispiel : `` `stdin, stdout, stderr, wait_thr = Open3.popen3 ([env,] cmd ... [, opts]) pid = wait_thr [: pid] # pid des gestarteten Prozesses ... stdin.close # stdin , stdout und stderr sollten in dieser Form explizit geschlossen werden. stdout.close stderr.close `` `Ich habe nur die Dokumentation zitiert." # stdin, stdout und stderr sollten in dieser Form explizit geschlossen werden. "
Peter H. Boling
61
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.170.170.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 [..].
Vielen Dank für den Tipp,% x [] zu verwenden. Es hat gerade ein Problem gelöst, bei dem ich in einem Ruby-Skript unter Mac OS X Back-Ticks verwendet habe. Wenn dasselbe Skript auf einem Windows-Computer mit Cygwin ausgeführt wurde, schlug es aufgrund der Back-Ticks fehl, funktionierte jedoch mit% x [].
Henrik Warne
22
Wenn Sie den Argumenten entkommen müssen, akzeptiert IO.popen in Ruby 1.9 auch ein Array:
Es schreibt nicht an stdout oder stderr. Versuchen wir dieses Beispiel ruby -e '%x{ls}'- beachten Sie, keine Ausgabe. (fyi %x{}ist gleichbedeutend mit backticks.)
ocodo
Das hat super geklappt. Die Verwendung von shwürde die Ausgabe an die Konsole (dh STDOUT) zurückgeben und zurückgeben. Das geht nicht.
Joshua Pinter
19
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 habe dies nur verwendet, um die Standardausgabe eines aws cli-Befehls zu lesen, um den json und nicht den offiziellen Rückgabewert von 'true' zu lesen
kraftydevil
14
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:
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 systemAusgabe 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 systemAufruf im zu testenden Code vergraben ist:
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 systemAnruf durch alles ersetzen , was intern anruft system. Sie können auch eine ähnliche Methode verwenden, um zu erfassen, stderrwenn Sie möchten.
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.
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:
oder
quelle
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
undOpen3.popen3
eine sichere / flüchtende Variante in Ruby 1.8 bereitstelle. In Ruby 1.9IO::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
: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
quelle
gets
Aufrufe sollten das Argument übergebennil
, da wir sonst nur die erste Zeile der Ausgabe erhalten. Also zBstdout.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 angehaltenstderr.write
und Ihr Programm bleibt hängenstdout.gets(nil)
.Nur für die Aufzeichnung, wenn Sie beide (Ausgabe und Operationsergebnis) möchten, können Sie Folgendes tun:
quelle
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()
oderOpen3.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 :Im
system
Gegensatz dazu entgeht die Funktion Argumenten bei korrekter Verwendung ordnungsgemäß :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
Undcapture3
Arbeit wiesystem
, kehrt aber zwei oder drei Argumente: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:Der Einfachheit halber können Sie
Open3.capture3()
eine Funktion einschließen , z.Beispiel:
Ergibt folgendes:
quelle
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
undcapture3
auch sie std * s automatisch schließen. (Zumindest bin ich nie auf das Problem gestoßen.)Open3#popen2
,popen2e
undpopen3
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.
% x [..] speichert andererseits die Ergebnisse des Befehls als Zeichenfolge:
Der Blog-Beitrag von Jay Fields erklärt ausführlich die Unterschiede zwischen der Verwendung von system, exec und% x [..].
quelle
Wenn Sie den Argumenten entkommen müssen, akzeptiert IO.popen in Ruby 1.9 auch ein Array:
In früheren Versionen können Sie Open3.popen3 verwenden :
Wenn Sie auch stdin bestehen müssen, sollte dies sowohl in 1.9 als auch in 1.8 funktionieren:
quelle
Sie verwenden Backticks:
quelle
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:
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.
quelle
Ich fand, dass Folgendes nützlich ist, wenn Sie den Rückgabewert benötigen:
Ich wollte speziell die Pids aller Java-Prozesse auf meinem Computer auflisten und habe Folgendes verwendet:
quelle
Wie Simon Hürlimann bereits erklärte , ist Open3 sicherer als Backticks usw.
Beachten Sie, dass das Blockformular stdin, stdout und stderr automatisch schließt - andernfalls müssten sie explizit geschlossen werden .
quelle
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:Diese Methode erfasst alle Ausgaben im angegebenen Block mithilfe einer Tempfile, um die tatsächlichen Daten zu speichern. Anwendungsbeispiel:
Sie können den
system
Anruf durch alles ersetzen , was intern anruftsystem
. Sie können auch eine ähnliche Methode verwenden, um zu erfassen,stderr
wenn Sie möchten.quelle
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.
quelle
Als direkten Systemersatz (...) können Sie Open3.popen3 (...) verwenden.
Weitere Diskussion: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
quelle
Ich habe dieses hier nicht gefunden, also hatte ich einige Probleme, die volle Ausgabe zu erhalten.
Quelle: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
quelle
quelle