ventajas sobre sintaxis rails programar programacion orientada official objetos libros español desventajas código con aprende ruby system call

sobre - Obteniendo salida de llamadas al sistema() en Ruby



sintaxis de ruby (15)

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 la forma de bloque cerrará automáticamente stdin, stdout y stderr, de lo contrario tendrían que cerrarse explícitamente .

Si invoco un comando usando Kernel#system en Ruby, ¿cómo obtengo su salida?

system("ls")



Descubrí que lo siguiente es útil si necesita el valor de retorno:

result = %x[ls] puts result

Quería específicamente 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]


La forma sencilla de hacerlo de forma correcta y segura es usar Open3.capture2() , Open3.capture2e() u Open3.capture3() .

El uso de backticks de ruby ​​y su alias %x NO ES SEGURO BAJO NINGUNA CIRCUNSTANCIA si se usa con datos no confiables. Es peligroso , sencillo y simple:

untrusted = "; date; echo" out = `echo #{untrusted}` # BAD untrusted = ''"; date; echo"'' out = `echo "#{untrusted}"` # BAD untrusted = "''; date; echo''" out = `echo ''#{untrusted}''` # BAD

La función del system , por el contrario, escapa 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 que mejor se adaptan a la tarea. Open3.capture2 , capture2e y capture3 funcionan como el 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 su comodidad, puede ajustar Open3.capture3() en 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'')

Rinde 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'')


Me gustaría ampliar y aclarar un poco la respuesta del caos .

Si rodea su comando con comillas invertidas, entonces no necesita (explícitamente) llamar al sistema () en absoluto. Los backticks ejecutan el comando y devuelven la salida como una cadena. A continuación, puede asignar el valor a una variable así:

output = `ls` p output

o

printf output # escapes newline chars


Mientras que usar backticks o popen es a menudo lo que realmente desea, en realidad no responde a la pregunta formulada. Puede haber razones válidas para capturar la salida del system (tal vez para pruebas automatizadas). Un poco de Google encontró una respuesta que pensé que publicaría aquí para beneficio de los demás.

Como lo necesitaba para probar, mi ejemplo utiliza una configuración de bloque para capturar la salida estándar, ya que la llamada real al system está oculta 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

Así que esto nos da un método que capturará 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

Por supuesto, puede reemplazar la llamada al system con cualquier cosa que pueda llamar internamente al system . También puede usar el mismo método para capturar stderr si lo desea.



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 introducir datos en la entrada estándar del programa, así como para leer su salida estándar.


Puede usar system () o% x [] dependiendo del tipo de resultado que necesite.

system () devolviendo verdadero si el comando fue encontrado y ejecutado exitosamente, falso de lo contrario.

>> 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 del blog de Jay Fields explica en detalle las diferencias entre el uso de system, exec y% x [..].


Si desea que la salida se redirija a un archivo utilizando Kernel#system , puede modificar los descriptores de esta forma:

redirigir stdout y stderr a un archivo (/ tmp / log) en modo agregado:

system(''ls -al'', :out => [''/tmp/log'', ''a''], :err => [''/tmp/log'', ''a''])

Para un comando de ejecución prolongada, esto almacenará la salida en tiempo real. También puede almacenar la salida utilizando un tubo IO y redirigirlo desde el sistema Kernel #.


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"


Solo para el registro, si desea ambos (resultado de salida y operación) puede hacer:

output=`ls no_existing_file` ; result=$?.success?


Tenga en cuenta que todas las soluciones en las que pasa una cadena que contiene los valores proporcionados por el usuario al system , %x[] etc. no son seguras. Inseguro realmente 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 el system y Open3.popen3 proporcionan una variante segura / de escape en Ruby 1.8. En Ruby 1.9 IO::popen también acepta un array.

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 la forma de bloque cerrará automáticamente stdin, stdout y stderr, de lo contrario tendrían que cerrarse explícitamente .

Más información aquí: Formando comandos de shell sanitario o llamadas de sistema en Ruby


Utiliza backticks:

`ls`


puts `date` puts $? Mon Mar 7 19:01:15 PST 2016 pid 13093 exit 0