Pregunta Llamar comandos de shell de Ruby


¿Cómo puedo llamar comandos de shell desde el interior de un programa de Ruby? ¿Cómo puedo obtener el resultado de estos comandos en Ruby?


883
2017-08-05 12:56


origen


Respuestas:


Esta explicación se basa en un comentario Ruby script de un amigo mío Si desea mejorar el guión, no dude en actualizarlo en el enlace.

En primer lugar, tenga en cuenta que cuando Ruby llama a un shell, por lo general llama /bin/sh, no Intento. Algunos sintaxis de Bash no son compatibles con /bin/sh en todos los sistemas.

Aquí hay formas de ejecutar un script de shell:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , comúnmente llamados backticks - `cmd`

    Esto es como muchos otros lenguajes, incluidos Bash, PHP y Perl.

    Devuelve el resultado del comando de shell.

    Documentos: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Sintaxis incorporada %x( cmd )

    Siguiendo el x el carácter es un delimitador, que puede ser cualquier carácter. Si el delimitador es uno de los caracteres (, [, {, o <, el literal consiste en los caracteres hasta el delimitador de cierre coincidente, teniendo en cuenta los pares de delimitadores anidados. Para todos los demás delimitadores, el literal comprende los caracteres hasta la próxima ocurrencia de la carácter delimitador Interpolación de cadenas #{ ... } esta permitido.

    Devuelve el resultado del comando de shell, al igual que los backticks.

    Documentos: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    Ejecuta el comando dado en una subshell.

    Devoluciones true si el comando fue encontrado y se ejecutó con éxito, false de otra manera.

    Documentos: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    Reemplaza el proceso actual ejecutando el comando externo dado.

    Devuelve ninguno, el proceso actual se reemplaza y nunca continúa.

    Documentos: http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

Aquí hay algunos consejos adicionales: $?, que es lo mismo que $CHILD_STATUS, accede al estado del último comando ejecutado del sistema si usa los palos de atrás, system() o %x{}. A continuación, puede acceder al exitstatus y pid propiedades:

$?.exitstatus

Para más lectura, ver:


1158
2017-08-05 14:42



La forma en que me gusta hacer esto es usar el %x literal, lo que hace que sea más fácil (¡y legible!) usar comillas en un comando, así:

directorylist = %x[find . -name '*test.rb' | sort]

Que, en este caso, completará la lista de archivos con todos los archivos de prueba en el directorio actual, que puede procesar como se esperaba:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

151
2017-08-05 14:08



Aquí hay un diagrama de flujo basado en esta respuesta. Ver también, utilizando script emular una terminal.

enter image description here


148
2018-05-19 17:01



Este es el mejor artículo en mi opinión sobre ejecutar scripts de shell en Ruby: "6 maneras de ejecutar los comandos de Shell en Ruby".

Si solo necesita obtener la salida, use palos de retroceso.

Necesitaba cosas más avanzadas como STDOUT y STDERR, así que usé la gema Open4. Tienes todos los métodos explicados allí.


58
2017-09-02 11:05



Mi favorito es Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

31
2017-09-18 17:47



Algunas cosas para pensar al elegir entre estos mecanismos son:

  1. ¿Solo quieres stdout o quieres ¿Necesita stderr también? o incluso separados?
  2. ¿Qué tan grande es tu producción? Quieres para mantener todo el resultado en la memoria?
  3. ¿Quieres leer algunos de tus salida mientras el subproceso está todavía ¿corriendo?
  4. ¿Necesitas códigos de resultado?
  5. ¿Necesitas un objeto de rubí que representa el proceso y te permite matarlo a pedido?

Puede necesitar cualquier cosa, desde simples backticks (``), system () y IO.popen a toda regla Kernel.fork/Kernel.exec con IO.pipe y IO.select.

También es posible que desee lanzar tiempos de espera en la mezcla si un subproceso tarda demasiado tiempo en ejecutarse.

Desafortunadamente, es mucho depende.


23
2017-08-07 05:10



Una opción más:

Cuando tú:

  • necesita stderr y stdout
  • no puedo / no usaré Open3 / Open4 (arrojan excepciones en NetBeans en mi Mac, ni idea de por qué)

Puede usar la redirección de shell:

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

los 2>&1 la sintaxis funciona a través de Linux, Mac y Windows desde los primeros días de MS-DOS.


19
2018-06-16 02:13



Definitivamente no soy un experto en Ruby, pero lo intentaré:

$ irb 
system "echo Hi"
Hi
=> true

También debería poder hacer cosas como:

cmd = 'ls'
system(cmd)

16
2017-08-05 13:24



Las respuestas anteriores ya son bastante buenas, pero realmente quiero compartir el siguiente artículo de resumen: "6 maneras de ejecutar los comandos de Shell en Ruby"

Básicamente, nos dice:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system y $?:

system 'false' 
puts $?

Backticks (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -- una gema:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

12
2018-06-07 02:07