Llamar comandos de shell de Ruby

votos
862

¿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?

Publicado el 05/08/2008 a las 13:56
fuente por usuario
En otros idiomas...                            


20 respuestas

votos
1k

Esta explicación se basa en un guión de Ruby comentado por 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 intérprete de comandos, normalmente llama /bin/sh, no a Bash. Algunos sintetizadores no admiten la sintaxis de Bash /bin/sh.

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 )

    El siguiente xcarácter es un delimitador, que puede ser cualquier carácter. Si el delimitador es uno de los personajes (, [, {, 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 siguiente aparición del carácter delimitador. La interpolación de cadenas #{ ... }está permitida.

    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.

    Devuelve truesi el comando fue encontrado y se ejecutó con éxito, de lo falsecontrario.

    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 a exitstatusy pidpropiedades:

$?.exitstatus

Para más lectura, ver:

Respondida el 05/08/2008 a las 15:42
fuente por usuario

votos
150

La forma en que me gusta hacer esto es usar el %xliteral, 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, 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
Respondida el 05/08/2008 a las 15:08
fuente por usuario

votos
140

Aquí está un diagrama de flujo en base a esta respuesta . Véase también, el uso scriptde emular un terminal .

introducir descripción de la imagen aquí

Respondida el 19/05/2016 a las 14:01
fuente por usuario

votos
58

Este es el mejor artículo en mi opinión sobre ejecutar scripts de shell en Ruby: " 6 formas de ejecutar 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í.

Respondida el 02/09/2008 a las 12:05
fuente por usuario

votos
31

Mi favorito es Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Respondida el 18/09/2008 a las 18:47
fuente por usuario

votos
23

Algunas cosas para pensar al elegir entre estos mecanismos son:

  1. ¿Desea stdout o necesita stderr también? o incluso separados?
  2. ¿Qué tan grande es tu producción? ¿Desea mantener todo el resultado en la memoria?
  3. ¿Desea leer parte de su salida mientras el subproceso aún se está ejecutando?
  4. ¿Necesitas códigos de resultado?
  5. ¿Necesitas un objeto de rubí que represente el proceso y te permite matarlo bajo demanda?

Puede necesitar cualquier cosa, desde simples backticks (``), system (), y IO.popenfull-blown Kernel.fork/ Kernel.execwith IO.pipey IO.select.

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

Desafortunadamente, eso depende mucho .

Respondida el 07/08/2008 a las 06:10
fuente por usuario

votos
19

Una de las opciones más:

Cuando tú:

  • necesitará stderr, así como la salida estándar
  • no puede / no va a usar Open3 / Open4 (que generan excepciones en NetBeans en mi Mac, ni idea de por qué)

Puede utilizar la redirección de consola:

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

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

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

Respondida el 16/06/2010 a las 03:13
fuente por usuario

votos
15

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)
Respondida el 05/08/2008 a las 14:24
fuente por usuario

votos
11

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

Básicamente, se nos dice:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemy $?:

system 'false' 
puts $?

Invertidas ( `):

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>]
Respondida el 07/06/2013 a las 03:07
fuente por usuario

votos
7

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

En primer lugar, tenga en cuenta que cuando Rubí llama a una concha, que por lo general llama /bin/sh, no Bash. Algunos sintaxis de Bash no es compatible con /bin/shen todos los sistemas.

Si es necesario utilizar Bash, inserte bash -c "your Bash-only command"el interior de su método de llamada deseada.

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")) Rubí debe cumplir el tinglado, pero se puede utilizar siempre system("bash ./my_script.sh")para asegurarse de que (aunque puede haber una ligera sobrecarga de /bin/shfuncionamiento /bin/bash, es probable que no se dé cuenta.

Respondida el 02/06/2017 a las 17:14
fuente por usuario

votos
7

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

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

Útil si necesitas algo simple.

El método que quieras usar depende exactamente de lo que intentas lograr; verifique los documentos para más detalles sobre los diferentes métodos.

Respondida el 05/08/2008 a las 14:57
fuente por usuario

votos
5

No olvide el spawncomando para crear un proceso en segundo plano para ejecutar el comando especificado. Incluso se puede esperar a su finalización mediante la Processclase y el vuelto pid:

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 doctor dice: Este método es similar a #systempero no espera a que el comando a fin.

Respondida el 04/11/2015 a las 12:04
fuente por usuario

votos
5

Usando las respuestas aquí y vinculado en la respuesta de Mihai, que arme una función que cumple estos requisitos:

  1. captura limpiamente stdout y stderr para que no se "fuga" cuando mi script se ejecuta desde la consola.
  2. Permite a los argumentos que se pasan a la shell como una matriz, así que no hay necesidad de preocuparse de escapar.
  3. Captura el estado de salida del comando así que está claro cuándo se ha producido un error.

Como beneficio adicional, éste también regresará STDOUT en los casos en que las salidas de comandos shell de éxito (0) y lo pone nada en la salida estándar. De esta manera, se diferencia de system, que simplemente devuelve trueen tales casos.

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"
Respondida el 21/02/2012 a las 00:36
fuente por usuario

votos
5

Podemos lograrlo en múltiples formas.

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

exec('ls ~')

Utilizando backticks or %x

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

El uso Kernel#systemde comandos, devuelve truesi tiene éxito, falsesi no tiene éxito y devuelve nilsi falla la ejecución del comando:

system('ls ~')
=> true
Respondida el 19/02/2012 a las 19:07
fuente por usuario

votos
4

Lo más fácil es, por ejemplo:

reboot = `init 6`
puts reboot
Respondida el 30/03/2017 a las 15:13
fuente por usuario

votos
3
  • acentos abiertos método `es la más fácil llamar a los comandos de shell de rubí. Devuelve el resultado del comando shell.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
Respondida el 16/02/2017 a las 06:58
fuente por usuario

votos
3

Si usted tiene un caso más complejo que el caso común (que no puede ser manejada con ``) entonces echa un vistazo Kernel.spawn() aquí . Este parece ser el más genérico / con todas las funciones proporcionada por valores de Ruby para ejecutar comandos externos.

Por ejemplo, puede utilizarlo para:

  • crear grupos de procesos (Windows)
  • redirigir hacia adentro, afuera, el error de archivos / cada-otra.
  • conjunto env vars, umask
  • dir cambiar antes de la ejecución de comandos
  • los límites de recursos de CPU / conjunto de datos / ...
  • Hacer todo lo que se puede hacer con otras opciones en otras respuestas, pero con más código.

Oficial documentación rubí tiene buenos ejemplos suficientes.

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)
Respondida el 11/12/2015 a las 11:57
fuente por usuario

votos
1

Dado un comando por ejemplo attrib

require 'open3'

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

He encontrado que si bien este método no es tan memorable como por ejemplo el sistema ( "thecommand") o thecommand entre comillas sencillas, una buena cosa acerca de este método en comparación con otros métodos .. es no parece por ejemplo acentos abiertos para que me pone' 'el comando corro / almacenar el comando Quiero correr en una variable, y el sistema ( 'thecommand') no parece que me deja la salida. Considerando que este método me permite hacer ambas cosas, y me deja la entrada estándar de acceso, 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

Respondida el 19/12/2017 a las 02:54
fuente por usuario

votos
0

En realidad, no una respuesta, pero tal vez alguien va a resultar útil, y su relación con esto.

Al utilizar los conocimientos tradicionales interfaz gráfica de usuario en Windows, yu tenga que llamar a los comandos de shell de rubyw, u siempre tendrá una ventana de cmd molesto apareciendo por menos de un segundo.

Para evitar este u puede utilizar

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

o

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

Ambos se almacenan los resultados de ipconfig dentro 'log.txt', pero sin ventanas se van a plantear.

T tendrá que require 'win32ole'dentro del guión.

system(), exec()Y spawn()se abrirá toda esa ventana molesto cuando se utilizan los conocimientos tradicionales y rubyw.

Respondida el 05/07/2018 a las 12:55
fuente por usuario

votos
-1

Aquí hay un fresco que yo uso en un script de rubí en OS X (de modo que pueda comenzar una secuencia de comandos y obtener una actualización, incluso después de alternar lejos de la ventana):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Respondida el 14/10/2014 a las 21:12
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more