Si llamo a un comando usando el sistema Kernel # en Ruby, ¿cómo obtengo su salida?
system("ls")
Si llamo a un comando usando el sistema Kernel # en Ruby, ¿cómo obtengo su salida?
system("ls")
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í:
output = `ls`
p output
o
printf output # escapes newline chars
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
y Open3.popen3
proporcionar una variante segura / de escape en Ruby 1.8. En Ruby 1.9 IO::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
:
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
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
gets
llamadas 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)
.
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 suspende stderr.write
y su programa se atasca stdout.gets(nil)
.
Solo para el registro, si desea ambos (resultado de salida y operación) puede hacer:
output=`ls no_existing_file` ; result=$?.success?
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()
o Open3.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:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
La system
funció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
, capture2e
y capture3
funciona 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 eol
rescue
end
end
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:
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 }
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
y capture3
también cerca de ellos STD * s automáticamente. (Por lo menos, nunca me encontré con el problema de mi parte.)
Open3#popen2
, popen2e
y popen3
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.
>> 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 [..] 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
La publicación de blog de Jay Fields explica en detalle las diferencias entre usar system, exec y% x [..].
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"
Usas backticks:
`ls`
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:
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.
Descubrí que lo siguiente es útil si necesita el valor de retorno:
result = %x[ls]
puts result
Específicamente quería enumerar los pids de todos los procesos de Java en mi máquina, y usé esto:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
Como Simon Hürlimann ya explicó , Open3 es más seguro que los backticks, etc.
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Tenga en cuenta que el formulario de bloqueo cerrará automáticamente stdin, stdout y stderr; de lo contrario, tendrían que cerrarse explícitamente .
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:
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
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 system
llamada con cualquier cosa que llame internamente system
. También podría usar un método similar para capturar stderr
si lo desea.
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 #.
Como reemplazo directo del sistema (...) puede usar Open3.popen3 (...)
Discusión adicional: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
No encontré este aquí, así que al agregarlo, tuve algunos problemas para obtener el resultado completo.
Puede redirigir STDERR a STDOUT si desea capturar STDERR usando la tecla de retroceso.
salida = `grep hosts / private / etc / * 2> & 1`
fuente: 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