Ejecución de comandos de línea de comandos dentro del script Ruby

92

¿Hay alguna forma de ejecutar comandos de línea de comandos a través de Ruby? Estoy tratando de crear un pequeño programa Ruby que marcaría y recibiría / enviaría a través de programas de línea de comando como 'pantalla', 'rcsz', etc.

Sería genial si pudiera vincular todo esto con Ruby (backend MySQL, etc.)

geetfun
fuente
Posible duplicado de comandos de shell
ymoreau

Respuestas:

209

Si. Hay varias formas:


a. Utilice %xo '' ':

%x(echo hi) #=> "hi\n"
%x(echo hi >&2) #=> "" (prints 'hi' to stderr)

`echo hi` #=> "hi\n"
`echo hi >&2` #=> "" (prints 'hi' to stderr)

Estos métodos devolverán stdout y redirigirán stderr al archivo del programa.


si. Utilizar system:

system 'echo hi' #=> true (prints 'hi')
system 'echo hi >&2' #=> true (prints 'hi' to stderr)
system 'exit 1' #=> nil

Este método regresa truesi el comando fue exitoso. Redirige toda la salida a la del programa.


C. Utilizar exec:

fork { exec 'sleep 60' } # you see a new process in top, "sleep", but no extra ruby process. 
exec 'echo hi' # prints 'hi'
# the code will never get here.

Eso reemplaza el proceso actual con el creado por el comando.


re. (ruby 1.9) uso spawn:

spawn 'sleep 1; echo one' #=> 430
spawn 'echo two' #=> 431
sleep 2
# This program will print "two\none".

Este método no espera a que el proceso salga y devuelve el PID.


mi. Utilizar IO.popen:

io = IO.popen 'cat', 'r+'
$stdout = io
puts 'hi'
$stdout = IO.new 0
p io.read(1)
io.close
# prints '"h"'.

Este método devolverá un IOobjeto que repite la entrada / salida de los nuevos procesos. También es actualmente la única forma que conozco de dar entrada al programa.


F. Usar Open3(en 1.9.2 y posteriores)

require 'open3'

stdout,stderr,status = Open3.capture3(some_command)
STDERR.puts stderr
if status.successful?
  puts stdout
else
  STDERR.puts "OH NO!"
end

Open3tiene varias otras funciones para obtener acceso explícito a los dos flujos de salida. Es similar a popen, pero te da acceso a stderr.

Adrian
fuente
Bono truco: io = IO.popen 'cat > out.log', 'r+'; escribe las salidas del comando en "out.log"
Narfanator
1
Cuáles son los pros y los contras de cada uno. ¿Cómo decido cuál usar? ¿Qué tal usar FileUtils[ ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html] ?
Ava
1
Estoy ejecutando un comando SVN usando exec. No quiero que la salida de exec aparezca en la consola. Quiero redirigirlo para poder almacenarlo como una variable y procesarlo. Cómo lo hago ?
stack1
2
status.successful? ya no me funciona en ruby ​​2.4, cambió a status.success? :)
DanielG
14

Hay algunas formas de ejecutar comandos del sistema en Ruby.

irb(main):003:0> `date /t` # surround with backticks
=> "Thu 07/01/2010 \n"
irb(main):004:0> system("date /t") # system command (returns true/false)
Thu 07/01/2010
=> true
irb(main):005:0> %x{date /t} # %x{} wrapper
=> "Thu 07/01/2010 \n"

Pero si realmente necesita realizar la entrada y salida con stdin / stdout del comando, probablemente querrá ver el IO::popenmétodo, que ofrece específicamente esa facilidad.

Mark Rushakoff
fuente
popen funciona bien si su aplicación solo tiene salida estándar. Si necesita más interacción o quiere hacer algo diferente con stdout, stdin, y particularmente stderr, también querrá mirar open3: ruby-doc.org/core/classes/Open3.html
Paul Rubel
7
 folder = "/"
 list_all_files = "ls -al #{folder}"
 output = `#{list_all_files}`
 puts output
Oh ho
fuente
2

Sí, esto es ciertamente factible, pero el método de implementación difiere dependiendo de si el programa de "línea de comando" en cuestión opera en "Pantalla completa" o en modo de línea de comando. Los programas escritos para la línea de comando tienden a leer STDIN y escribir en STDOUT. Estos se pueden llamar directamente dentro de Ruby usando los métodos estándar de comillas invertidas y / o llamadas al sistema / exec.

Si el programa funciona en modo "Pantalla completa" como screen o vi, entonces el enfoque tiene que ser diferente. Para programas como este, debe buscar una implementación Ruby de la biblioteca "espera". Esto le permitirá crear un script de lo que espera ver en la pantalla y qué enviar cuando vea aparecer esas cadenas en particular en la pantalla.

Es poco probable que este sea el mejor enfoque y probablemente debería mirar lo que está tratando de lograr y encontrar la biblioteca / gema relevante para hacerlo en lugar de intentar automatizar una aplicación de pantalla completa existente. Como ejemplo, " Necesito ayuda con las comunicaciones del puerto serie en Ruby " trata de las comunicaciones del puerto serie, un precursor para marcar si eso es lo que desea lograr utilizando los programas específicos que mencionó.

Steve Weet
fuente
Una versión simple de Expect está disponible en Ruby usando su módulo Pty incorporado .
The Tin Man
0

El método más utilizado que está usando Open3aquí es mi versión editada de código del código anterior con algunas correcciones:

require 'open3'
puts"Enter the command for execution"
some_command=gets
stdout,stderr,status = Open3.capture3(some_command)
STDERR.puts stderr
if status.success?
  puts stdout
else
  STDERR.puts "ERRRR"
end
Keshav Maheshwari
fuente