Obtener resultados de llamadas al sistema () en Ruby
Si llamo a un comando usando el sistema Kernel# en Ruby, ¿cómo obtengo su resultado?
system("ls")
Me gustaría ampliar y aclarar un poco la respuesta del caos .
Si rodea su comando con comillas invertidas, entonces no necesita llamar (explícitamente) a system() en absoluto. Las comillas invertidas ejecutan el comando y devuelven la salida como una cadena. Luego puedes asignar el valor a una variable de esta manera:
output = `ls`
p output
o
printf output # escapes newline chars
Tenga en cuenta que todas las soluciones en 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 que se ejecute en el contexto y con todos los permisos del programa.
Por lo que puedo decir, solo system
proporciono Open3.popen3
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, probablemente desee utilizar 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 bloque 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
Solo para que conste, si desea ambos (salida y resultado de la operación), puede hacer:
output=`ls no_existing_file` ; result=$?.success?
La forma sencilla de hacer esto de forma correcta y segura es usar Open3.capture2()
, Open3.capture2e()
o Open3.capture3()
.
El uso de las comillas invertidas de Ruby y su %x
alias NO ES SEGURO BAJO NINGUNA CIRCUNSTANCIA si se usa con datos que no son de confianza. Es PELIGROSO , simple y llanamente:
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 correctamente a los argumentos 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 esta última es complicado y complicado.
La mejor respuesta en este hilo hasta ahora menciona Open3, pero no las funciones que mejor se adaptan a la tarea. Open3.capture2
y funciona como , pero devuelve dos o tres argumentos capture2e
:capture3
system
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 incluir 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')