Obtener resultados de llamadas al sistema () en Ruby

Resuelto Macha asked hace 15 años • 19 respuestas

Si llamo a un comando usando el sistema Kernel# en Ruby, ¿cómo obtengo su resultado?

system("ls")
Macha avatar Mar 27 '09 22:03 Macha
Aceptado

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
Craig Walker avatar Oct 14 '2009 00:10 Craig Walker

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 systemproporciono Open3.popen3una 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, 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

Simon Hürlimann avatar May 11 '2011 21:05 Simon Hürlimann

Solo para que conste, si desea ambos (salida y resultado de la operación), puede hacer:

output=`ls no_existing_file` ;  result=$?.success?
FernandoFabreti avatar Oct 17 '2010 00:10 FernandoFabreti

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 %xalias 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 systemfunció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.capture2y funciona como , pero devuelve dos o tres argumentos capture2e:capture3system

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')
Denis de Bernardy avatar Nov 15 '2013 12:11 Denis de Bernardy