Si rodea su comando con backticks, entonces no necesita llamar (explícitamente) a system () en absoluto. Los backticks ejecutan el comando y devuelven la salida como una cadena. Luego puede asignar el valor a una variable así:
¿Qué pasa si necesito dar una variable como parte de mi comando? Es decir, ¿en qué se traduciría algo como el sistema ("ls" + nombre de archivo) cuando se van a utilizar los backticks?
Vijay Dev
47
Usted puede hacer la evaluación de expresiones tal como lo haría con las cadenas regulares: ls #{filename}.
Craig Walker
36
Esta respuesta no es aconsejable: presenta el nuevo problema de la entrada de usuario no higienizada.
Dogweather
44
@Dogweather: eso puede ser cierto, pero ¿es diferente de cualquiera de los otros métodos?
Craig Walker
20
si desea captar stderr simplemente ponga 2> & 1 al final de su comando. por ejemplo, salida =command 2>&1
micred
243
¡Tenga en cuenta que todas las soluciones a las que pasa una cadena que contiene valores proporcionados por el usuario system, %x[]etc., no son seguras! Inseguro en realidad significa: el usuario puede activar el código para ejecutarse en el contexto y con todos los permisos del programa.
Por lo que puedo decir solo systemy Open3.popen3proporcionar una variante segura / de escape en Ruby 1.8. En Ruby 1.9 IO::popentambién acepta una matriz.
Simplemente pase cada opción y argumento como una matriz a una de estas llamadas.
Si no solo necesita el estado de salida, sino también el resultado que probablemente quiera usar Open3.popen3:
Esta es la única respuesta que realmente responde a la pregunta y resuelve el problema sin introducir otras nuevas (entrada no higiénica).
Dogweather
2
¡Gracias! Este es el tipo de respuesta que esperaba. Una corrección: las getsllamadas deben pasar el argumento nil, ya que de lo contrario solo obtendremos la primera línea de la salida. Entonces, por ejemplo stdout.gets(nil).
¿Alguien sabe si algo cambió en Ruby 2.0 o 2.1? Ediciones o comentarios serían apreciados ;-)
Simon Hürlimann
1
Creo que en la discusión Open3.popen3falta un problema importante: si tiene un subproceso que escribe más datos en stdout de los que puede contener una tubería, el subproceso se suspende stderr.writey su programa se atasca stdout.gets(nil).
hagello
165
Solo para el registro, si desea ambos (resultado de salida y operación) puede hacer:
Esto es exactamente lo que estaba buscando. Gracias.
jdl
12
Eso solo captura stdout, y stderr va a la consola. Para obtener stderr, use: output=`ls no_existing_file 2>&1`; result=$?.success?
peterept
8
Esta respuesta no es segura y no debe usarse: si el comando no es una constante, entonces la sintaxis de retroceso puede causar un error, posiblemente una vulnerabilidad de seguridad. (E incluso si es una constante, probablemente hará que alguien lo use por una no constante más tarde y cause un error). Vea la respuesta de Simon Hürlimann para obtener una solución correcta.
Greg Price
23
Felicitaciones a Greg Price por comprender la necesidad de escapar de la entrada del usuario, pero no es correcto decir que esta respuesta como escrita no es segura. El método Open3 mencionado es más complicado e introduce más dependencias, y el argumento de que alguien "lo usará para una no constante más tarde" es un hombre de paja. Es cierto que probablemente no los usaría en una aplicación Rails, pero para un script de utilidad de sistema simple sin posibilidad de entrada del usuario no confiable, los backticks están perfectamente bien y nadie debe sentirse mal por usarlos.
Usar los backticks de ruby y su %xalias NO SON SEGUROS BAJO NINGUNA CIRCUNSTANCIA si se usan con datos no confiables. Es PELIGROSO , así de simple:
untrusted ="; date; echo"
out =`echo #{untrusted}`# BAD
untrusted ='"; date; echo"'
out =`echo "#{untrusted}"`# BAD
untrusted ="'; date; echo'"
out =`echo '#{untrusted}'`# BAD
La systemfunción, por el contrario, escapa a los argumentos correctamente si se usa correctamente :
ret = system "echo #{untrusted}"# BAD
ret = system 'echo', untrusted # good
El problema es que devuelve el código de salida en lugar de la salida, y capturar este último es complicado y desordenado.
La mejor respuesta en este hilo hasta ahora menciona Open3, pero no las funciones más adecuadas para la tarea. Open3.capture2, capture2ey capture3funciona como system, pero devuelve dos o tres argumentos:
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
Otro menciona IO.popen(). La sintaxis puede ser torpe en el sentido de que quiere una matriz como entrada, pero también funciona:
out = IO.popen(['echo', untrusted]).read # good
Para mayor comodidad, puede ajustar Open3.capture3()una función, por ejemplo:
## 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
Ejemplo:
p system('foo')
p syscall('foo')
p system('which','foo')
p syscall('which','foo')
p system('which','which')
p syscall('which','which')
Produce lo siguiente:
nilnilfalsefalse/usr/bin/which <— stdout from system('which','which')true<- p system('which','which')"/usr/bin/which"<- p syscall('which','which')
Esta es la respuesta correcta. También es el más informativo. Lo único que falta es una advertencia sobre cerrar los std * s. Vea este otro comentario : require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } tenga en cuenta que el formulario de bloqueo se cerrará automáticamente stdin, stdout y stderr; de lo contrario, tendrían que cerrarse explícitamente .
Peter H. Boling
@ PeterH.Boling: Mejor yo sepa, el capture2, capture2ey capture3también cerca de ellos STD * s automáticamente. (Por lo menos, nunca me encontré con el problema de mi parte.)
Denis de Bernardy
sin usar el formulario de bloque, no hay forma de que una base de código sepa cuándo se debe cerrar algo, por lo que dudo mucho que se esté cerrando. Probablemente nunca haya tenido un problema porque no cerrarlos no causará problemas en un proceso de corta duración, y si reinicia un proceso de ejecución prolongada con la suficiente frecuencia, otto tampoco aparecerá allí a menos que esté abriendo std * s en un bucle. Linux tiene un límite alto de descriptor de archivo, que puede alcanzar, pero hasta que no lo vea no verá el "error".
Peter H. Boling
2
@ PeterH.Boling: No, no, vea el código fuente. Las funciones son solo envoltorios Open3#popen2, popen2ey popen3con un bloque predefinido: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/…
Denis de Bernardy
1
@Dennis de Barnardy Quizás te perdiste que vinculé a la misma documentación de clase (aunque para Ruby 2.0.0, y un método diferente. Ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/… Del ejemplo : `` `stdin, stdout, stderr, wait_thr = Open3.popen3 ([env,] cmd ... [, opts]) pid = wait_thr [: pid] # pid del proceso iniciado ... stdin.close # stdin , stdout y stderr deben cerrarse explícitamente en este formulario. stdout.close stderr.close `` `` Estaba citando la documentación ''. # stdin, stdout y stderr deberían cerrarse explícitamente en este formulario "
Peter H. Boling
61
Puede usar system () o% x [] dependiendo del tipo de resultado que necesite.
system () devuelve verdadero si el comando se encontró y se ejecutó correctamente, de lo contrario, falso.
>> 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 [..] por otro lado guarda los resultados del comando como una cadena:
>> 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
Gracias por el consejo de usar% x []. Simplemente resolvió un problema que tenía cuando usaba ticks en un script ruby en Mac OS X. Cuando ejecuté el mismo script en una máquina Windows con Cygwin, falló debido a los ticks posteriores, pero funcionó con% x [].
Henrik Warne
22
Si necesita escapar de los argumentos, en Ruby 1.9 IO.popen también acepta una matriz:
p IO.popen(["echo","it's escaped"]).read
En versiones anteriores puedes usar Open3.popen3 :
require "open3"Open3.popen3("echo","it's escaped"){|i, o| p o.read }
Si también necesita pasar stdin, esto debería funcionar tanto en 1.9 como en 1.8:
out = IO.popen("xxd -p","r+"){|io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
No escribe en stdout o stderr. Probemos este ejemplo ruby -e '%x{ls}': tenga en cuenta que no hay salida. (FYI %x{}es equivalente a backticks.)
Ocodo
Esto funcionó muy bien. El uso shreflejaría la salida a la consola (es decir, STDOUT) y la devolvería. Esto no lo hace.
Joshua Pinter
19
Otra forma es:
f = open("|ls")
foo = f.read()
Tenga en cuenta que ese es el carácter "pipe" antes de "ls" en abierto. Esto también se puede utilizar para alimentar datos en la entrada estándar del programa, así como leer su salida estándar.
Si bien usar backticks o popen es a menudo lo que realmente quieres, en realidad no responde la pregunta que se hace. Puede haber razones válidas para capturar systemresultados (tal vez para pruebas automatizadas). Un pequeño Google encontró una respuesta que pensé que publicaría aquí en beneficio de los demás.
Como necesitaba esto para probar, mi ejemplo utiliza una configuración de bloque para capturar la salida estándar ya que la systemllamada real está enterrada en el código que se está probando:
Este método captura cualquier salida en el bloque dado usando un archivo temporal para almacenar los datos reales. Ejemplo de uso:
captured_content = capture_stdout do
system 'echo foo'end
puts captured_content
Puede reemplazar la systemllamada con cualquier cosa que llame internamente system. También podría usar un método similar para capturar stderrsi lo desea.
Para un comando de larga ejecución, esto almacenará la salida en tiempo real. También puede almacenar el resultado utilizando un IO.pipe y redirigirlo desde el sistema Kernel #.
Respuestas:
Me gustaría ampliar y aclarar un poco la respuesta del caos .
Si rodea su comando con backticks, entonces no necesita llamar (explícitamente) a system () en absoluto. Los backticks ejecutan el comando y devuelven la salida como una cadena. Luego puede asignar el valor a una variable así:
o
fuente
ls #{filename}
.command 2>&1
¡Tenga en cuenta que todas las soluciones a las que pasa una cadena que contiene valores proporcionados por el usuario
system
,%x[]
etc., no son seguras! Inseguro en realidad significa: el usuario puede activar el código para ejecutarse en el contexto y con todos los permisos del programa.Por lo que puedo decir solo
system
yOpen3.popen3
proporcionar una variante segura / de escape en Ruby 1.8. En Ruby 1.9IO::popen
también acepta una matriz.Simplemente pase cada opción y argumento como una matriz a una de estas llamadas.
Si no solo necesita el estado de salida, sino también el resultado que probablemente quiera usar
Open3.popen3
:Tenga en cuenta que el formulario de bloqueo cerrará automáticamente stdin, stdout y stderr; de lo contrario, tendrían que cerrarse explícitamente .
Más información aquí: Formación de comandos de shell sanitarios o llamadas al sistema en Ruby
fuente
gets
llamadas deben pasar el argumentonil
, ya que de lo contrario solo obtendremos la primera línea de la salida. Entonces, por ejemplostdout.gets(nil)
.Open3.popen3
falta un problema importante: si tiene un subproceso que escribe más datos en stdout de los que puede contener una tubería, el subproceso se suspendestderr.write
y su programa se atascastdout.gets(nil)
.Solo para el registro, si desea ambos (resultado de salida y operación) puede hacer:
fuente
output=`ls no_existing_file 2>&1`; result=$?.success?
La forma más sencilla de hacer esto correctamente y de forma segura es utilizar
Open3.capture2()
,Open3.capture2e()
oOpen3.capture3()
.Usar los backticks de ruby y su
%x
alias NO SON SEGUROS BAJO NINGUNA CIRCUNSTANCIA si se usan con datos no confiables. Es PELIGROSO , así de simple:La
system
función, por el contrario, escapa a los argumentos correctamente si se usa correctamente :El problema es que devuelve el código de salida en lugar de la salida, y capturar este último es complicado y desordenado.
La mejor respuesta en este hilo hasta ahora menciona Open3, pero no las funciones más adecuadas para la tarea.
Open3.capture2
,capture2e
ycapture3
funciona comosystem
, pero devuelve dos o tres argumentos:Otro menciona
IO.popen()
. La sintaxis puede ser torpe en el sentido de que quiere una matriz como entrada, pero también funciona:Para mayor comodidad, puede ajustar
Open3.capture3()
una función, por ejemplo:Ejemplo:
Produce lo siguiente:
fuente
require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
tenga en cuenta que el formulario de bloqueo se cerrará automáticamente stdin, stdout y stderr; de lo contrario, tendrían que cerrarse explícitamente .capture2
,capture2e
ycapture3
también cerca de ellos STD * s automáticamente. (Por lo menos, nunca me encontré con el problema de mi parte.)Open3#popen2
,popen2e
ypopen3
con un bloque predefinido: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/…Puede usar system () o% x [] dependiendo del tipo de resultado que necesite.
system () devuelve verdadero si el comando se encontró y se ejecutó correctamente, de lo contrario, falso.
% x [..] por otro lado guarda los resultados del comando como una cadena:
La publicación de blog de Jay Fields explica en detalle las diferencias entre usar system, exec y% x [..].
fuente
Si necesita escapar de los argumentos, en Ruby 1.9 IO.popen también acepta una matriz:
En versiones anteriores puedes usar Open3.popen3 :
Si también necesita pasar stdin, esto debería funcionar tanto en 1.9 como en 1.8:
fuente
Usas backticks:
fuente
ruby -e '%x{ls}'
: tenga en cuenta que no hay salida. (FYI%x{}
es equivalente a backticks.)sh
reflejaría la salida a la consola (es decir, STDOUT) y la devolvería. Esto no lo hace.Otra forma es:
Tenga en cuenta que ese es el carácter "pipe" antes de "ls" en abierto. Esto también se puede utilizar para alimentar datos en la entrada estándar del programa, así como leer su salida estándar.
fuente
Descubrí que lo siguiente es útil si necesita el valor de retorno:
Específicamente quería enumerar los pids de todos los procesos de Java en mi máquina, y usé esto:
fuente
Como Simon Hürlimann ya explicó , Open3 es más seguro que los backticks, etc.
Tenga en cuenta que el formulario de bloqueo cerrará automáticamente stdin, stdout y stderr; de lo contrario, tendrían que cerrarse explícitamente .
fuente
Si bien usar backticks o popen es a menudo lo que realmente quieres, en realidad no responde la pregunta que se hace. Puede haber razones válidas para capturar
system
resultados (tal vez para pruebas automatizadas). Un pequeño Google encontró una respuesta que pensé que publicaría aquí en beneficio de los demás.Como necesitaba esto para probar, mi ejemplo utiliza una configuración de bloque para capturar la salida estándar ya que la
system
llamada real está enterrada en el código que se está probando:Este método captura cualquier salida en el bloque dado usando un archivo temporal para almacenar los datos reales. Ejemplo de uso:
Puede reemplazar la
system
llamada con cualquier cosa que llame internamentesystem
. También podría usar un método similar para capturarstderr
si lo desea.fuente
Si desea que la salida se redirija a un archivo utilizando
Kernel#system
, puede modificar descriptores como este:redirigir stdout y stderr a un archivo (/ tmp / log) en modo append:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Para un comando de larga ejecución, esto almacenará la salida en tiempo real. También puede almacenar el resultado utilizando un IO.pipe y redirigirlo desde el sistema Kernel #.
fuente
Como reemplazo directo del sistema (...) puede usar Open3.popen3 (...)
Discusión adicional: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
fuente
No encontré este aquí, así que al agregarlo, tuve algunos problemas para obtener el resultado completo.
fuente: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
fuente
fuente