tutorial ruby shell interop

tutorial - ruby system command



Llamando comandos de shell desde Ruby (20)

Algunas cosas para pensar al elegir entre estos mecanismos son:

  1. ¿Solo quieres stdout o necesitas stderr también? o incluso separados?
  2. ¿Qué tan grande es su salida? ¿Quieres mantener todo el resultado en la memoria?
  3. ¿Desea leer algo de su salida mientras el subproceso todavía se está ejecutando?
  4. ¿Necesita códigos de resultados?
  5. ¿Necesita un objeto de rubí que represente el proceso y le permita matarlo a demanda?

Es posible que necesite cualquier cosa, desde simples backticks (``), system () y IO.popen hasta Kernel.fork / Kernel.exec con IO.pipe y IO.select IO.pipe IO.select .

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

Desafortunadamente, depende mucho.

¿Cómo invoco comandos de shell desde dentro de un programa Ruby? ¿Cómo puedo obtener la salida de estos comandos de nuevo en Ruby?


Aquí hay un diagrama de flujo basado en esta respuesta . Véase también, usando script para emular un terminal .


Aquí hay una genial que uso en una secuencia de comandos ruby ​​en OS X (para que pueda iniciar una secuencia de comandos y obtener una actualización incluso después de haber salido de la ventana):

cmd = %Q|osascript -e ''display notification "Server was reset" with title "Posted Update"''| system ( cmd )


Dado un comando, por ejemplo, atrib

require ''open3'' a="attrib" Open3.popen3(a) do |stdin, stdout, stderr| puts stdout.read end

Descubrí que si bien este método no es tan memorable como, por ejemplo, el sistema ("comando") o el comando en backticks, algo bueno acerca de este método en comparación con otros métodos ... es que, por ejemplo, Backticks no me deja "poner ''el comando Ejecuto / almaceno el comando que deseo ejecutar en una variable, y el sistema ("comando") no parece permitirme obtener la salida. Mientras que este método me permite hacer ambas cosas, y me permite acceder a stdin, stdout y stderr de forma independiente.

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html


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

$ irb system "echo Hi" Hi => true

También deberías poder hacer cosas como:

cmd = ''ls'' system(cmd)


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

Primero, tenga en cuenta que cuando Ruby llama a un shell, normalmente llama /bin/sh , no Bash. Alguna sintaxis de Bash no es compatible 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 llamado `cmd`

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

    Devuelve el resultado del comando de shell.

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

    value = `echo ''hi''` value = `#{cmd}`

  2. Sintaxis incorporada, %x( cmd )

    Seguir el carácter x 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 delimitadores anidados. Para todos los demás delimitadores, el literal comprende los caracteres hasta el Próxima aparición del carácter delimitador. Se permite la interpolación de cadena #{ ... } .

    Devuelve el resultado del comando de shell, al igual que las comillas invertidas.

    Docs: 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.

    Devuelve true si el comando fue encontrado y ejecutado exitosamente, false contrario.

    Docs: 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.

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

    Docs: 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 usas las comillas invertidas, system() o %x{} . A continuación, puede acceder a las propiedades exitstatus y pid :

$?.exitstatus

Para más lectura ver:


Este es el mejor artículo en mi opinión sobre la ejecución de scripts de shell en Ruby: " http://tech.natemurray.com/2007/03/ruby-shell-commands.html ".

Si solo necesitas obtener la salida usa backticks.

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


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

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

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

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


La forma más fácil es, por ejemplo:

reboot = `init 6` puts reboot


Las respuestas anteriores ya son bastante buenas, pero realmente quiero compartir el siguiente artículo de resumen: " http://tech.natemurray.com/2007/03/ruby-shell-commands.html "

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 joya:

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


Mi favorito es Open3

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


No es realmente una respuesta, pero quizás a alguien le resulte útil, y se trata de esto.

Al usar TK GUI en Windows, y necesitas llamar a los comandos de shell desde rubyw, u siempre tendrá una ventana de cmd molesta que aparecerá por menos de un segundo.

Para evitar esto puedes usar

WIN32OLE.new(''Shell.Application'').ShellExecute(''ipconfig > log.txt'','''','''',''open'',0)

o

WIN32OLE.new(''WScript.Shell'').Run(''ipconfig > log.txt'',0,0)

Ambos almacenarán la salida de ipconfig dentro de ''log.txt'', pero no aparecerá ninguna ventana.

Necesitará U require ''win32ole'' dentro de su script.

system() , exec() y spawn() mostrarán esa ventana molesta cuando usen TK y rubyw.


No olvide el comando spawn para crear un proceso en segundo plano para ejecutar el comando especificado. Incluso puede esperar su finalización utilizando la clase de Process y el pid devuelto:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2") Process.wait pid pid = spawn(RbConfig.ruby, "-eputs''Hello, world!''") Process.wait pid

El documento dice: este método es similar a #system pero no espera a que finalice el comando.


Podemos lograrlo de múltiples maneras.

Usando Kernel#exec , nada después de ejecutar este comando:

exec(''ls ~'')

Usando backticks or %x

`ls ~` => "Applications/nDesktop/nDocuments" %x(ls ~) => "Applications/nDesktop/nDocuments"

Usando Kernel#system comando del Kernel#system , devuelve true si es exitoso, false si no tiene éxito y devuelve nil si la ejecución del comando falla:

system(''ls ~'') => true


Si realmente necesitas Bash, por la nota en la "mejor" respuesta.

Primero, tenga en cuenta que cuando Ruby llama a un shell, normalmente llama /bin/sh , no Bash. Alguna sintaxis de Bash no es compatible con /bin/sh en todos los sistemas.

Si necesita usar Bash, inserte bash -c "your Bash-only command" dentro del método de llamada deseado.

quick_output = system("ls -la")

quick_bash = system("bash -c ''ls -la''")

Probar:

system("echo $SHELL") system(''bash -c "echo $SHELL"'')

O si está ejecutando un archivo de script existente (por ejemplo, script_output = system("./my_script.sh") ) Ruby debe cumplir con el shebang, pero siempre puede usar el system("bash ./my_script.sh") para asegurarse ( aunque puede haber una ligera sobrecarga de /bin/sh running /bin/bash , probablemente no lo notará.


Si tiene un caso más complejo que el caso común (que no se puede manejar con `` ), consulte Kernel.spawn() here . Este parece ser el producto más genérico / completo proporcionado por Ruby para ejecutar comandos externos.

Por ejemplo, puedes usarlo para:

  • crear grupos de procesos (Windows)
  • redirigir dentro, fuera, error a archivos / entre sí.
  • establecer env vars, umask
  • cambiar dir antes de ejecutar comando
  • establecer limites de recursos para CPU / data / ...
  • Haga todo lo que se puede hacer con otras opciones en otras respuestas, pero con más código.

La here oficial de here tiene buenos ejemplos.

env: hash name => val : set the environment variable name => nil : unset the environment variable command...: commandline : command line string which is passed to the standard shell cmdname, arg1, ... : command name and one or more arguments (no shell) [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell) options: hash clearing environment variables: :unsetenv_others => true : clear environment variables except specified by env :unsetenv_others => false : dont clear (default) process group: :pgroup => true or 0 : make a new process group :pgroup => pgid : join to specified process group :pgroup => nil : dont change the process group (default) create new process group: Windows only :new_pgroup => true : the new process is the root process of a new process group :new_pgroup => false : dont create a new process group (default) resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit. :rlimit_resourcename => limit :rlimit_resourcename => [cur_limit, max_limit] current directory: :chdir => str umask: :umask => int redirection: key: FD : single file descriptor in child process [FD, FD, ...] : multiple file descriptor in child process value: FD : redirect to the file descriptor in parent process string : redirect to file with open(string, "r" or "w") [string] : redirect to file with open(string, File::RDONLY) [string, open_mode] : redirect to file with open(string, open_mode, 0644) [string, open_mode, perm] : redirect to file with open(string, open_mode, perm) [:child, FD] : redirect to the redirected file descriptor :close : close the file descriptor in child process FD is one of follows :in : the file descriptor 0 which is the standard input :out : the file descriptor 1 which is the standard output :err : the file descriptor 2 which is the standard error integer : the file descriptor of specified the integer io : the file descriptor specified as io.fileno file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not :close_others => false : inherit fds (default for system and exec) :close_others => true : dont inherit (default for spawn and IO.popen)


También puede usar los operadores de comillas invertidas (`), similares a Perl:

directoryListing = `ls /` puts directoryListing # prints the contents of the root directory

Práctico si necesitas algo simple.

El método que desee utilizar depende exactamente de lo que intenta lograr; Consulte la documentación para obtener más detalles acerca de los diferentes métodos.


Una opción más:

Cuando tú:

  • Necesito stderr así como stdout
  • No puedo / no usaré Open3 / Open4 (lanzan excepciones en NetBeans en mi Mac, ni idea de por qué)

Puede utilizar 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"

La sintaxis 2>&1 funciona en Linux , Mac y Windows desde los primeros días de MS-DOS.


Usando las respuestas aquí y enlazadas en la respuesta de Mihai, armé una función que cumple con estos requisitos:

  1. Captura perfectamente STDOUT y STDERR para que no se "escapen" cuando mi script se ejecuta desde la consola.
  2. Permite que los argumentos se pasen al shell como una matriz, por lo que no hay que preocuparse por escapar.
  3. Captura el estado de salida del comando para que quede claro cuando se ha producido un error.

Como beneficio adicional, este también devolverá STDOUT en los casos en que el comando de shell salga correctamente (0) y ponga cualquier cosa en STDOUT. De esta manera, difiere del system , que simplemente devuelve true en tales casos.

El código sigue. La función específica es system_quietly :

require ''open3'' class ShellError < StandardError; end #actual function: def system_quietly(*cmd) exit_status=nil err=nil out=nil Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread| err = stderr.gets(nil) out = stdout.gets(nil) [stdin, stdout, stderr].each{|stream| stream.send(''close'')} exit_status = wait_thread.value end if exit_status.to_i > 0 err = err.chomp if err raise ShellError, err elsif out return out.chomp else return true end end #calling it: begin puts system_quietly(''which'', ''ruby'') rescue ShellError abort "Looks like you don''t have the `ruby` command. Odd." end #output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"


  • El método de backticks `es el más fácil de llamar a comandos de shell desde ruby. Devuelve el resultado del comando shell.

    url_request = ''http://google.com'' result_of_shell_command = `curl #{url_request}`