Si bien esta pregunta es útil, no se hace bien. Ruby tiene muchas formas de llamar subcapas que están bien documentadas y se encuentran fácilmente leyendo la documentación de Kernel y Open3 y buscando aquí en SO.
The Tin Man el
1
Lamentablemente este tema es bastante complejo. Open3( docs ) es la mejor opción para la mayoría de las situaciones, IMO, pero en versiones anteriores de Ruby, no respetará un modificado PATH( bugs.ruby-lang.org/issues/8004 ), y dependiendo de cómo pase los argumentos (específicamente , si utiliza hash opts con palabras clave que no son), puede romperse. Pero, si te encuentras con esas situaciones, estás haciendo algo bastante avanzado y puedes descubrir qué hacer leyendo la implementación de Open3.
Joshua Cheek
3
Me sorprende que nadie haya mencionado Shellwords.escape( doc ). No querrá insertar la entrada del usuario directamente en los comandos de la shell: ¡escape primero! Ver también comando inyección .
Kelvin
Respuestas:
1319
Esta explicación se basa en un script Ruby comentado de 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, generalmente llama /bin/sh, no Bash. Alguna sintaxis de Bash no es compatible /bin/shcon todos los sistemas.
Aquí hay formas de ejecutar un script de shell:
cmd ="echo 'hi'"# Sample string that can be used
Kernel#` , comúnmente llamados backticks - `cmd`
Esto es como muchos otros lenguajes, incluidos Bash, PHP y Perl.
Devuelve el resultado (es decir, salida estándar) del comando de shell.
Siguiendo al xpersonaje hay un delimitador, que puede ser cualquier personaje. 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 próxima aparición del carácter delimitador. #{ ... }Se permite la interpolación de cadenas .
Devuelve el resultado (es decir, la salida estándar) del comando de shell, al igual que los backticks.
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 por el sistema si usa las teclas de retroceso, system()o %x{}. Luego puede acceder a las propiedades exitstatusy pid:
Necesito registrar las salidas de mi ejecutable en el servidor de producción pero no encontré ninguna manera. Solía pone #{cmd}y logger.info ( #{cmd}). ¿Hay alguna forma de registrar sus resultados en la producción?
En aras de la integridad (como pensé por primera vez, esto también sería un comando Ruby): Rake tiene sh que hace "Ejecutar el comando del sistema cmd. Si se dan varios argumentos, el comando no se ejecuta con el shell (la misma semántica que Kernel :: exec y Kernel :: system) ".
sschuberth
40
Los backticks no capturan STDERR por defecto. Agregue `2> & 1` al comando si desea capturar
Andrei Botalov
14
Creo que esta respuesta se mejoraría ligeramente si dijera que los backticks y% x devolvieron el "resultado", en lugar del "resultado", del comando dado. Este último podría confundirse con el estado de salida. O solo soy yo?
Wow jaja. Muy útil aunque el hecho de que esto tenga que existir es lamentable
Josh Bodah
Como nota al margen, creo que el método de desove () encuentra en muchos lugares diferentes (por ejemplo, Kernely Processpara ser más versátil Es más o menos lo mismo con. PTY.spawn(), Pero más genérico.
Smar
160
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 espera:
directorylist.each do|filename|
filename.chomp!# work with fileend
Lo anterior no me funciona. `` <principal> '': método indefinido, each' for :String (NoMethodError) ¿cómo funcionó para usted? Estoy usando ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]¿Está seguro de que el comando devuelve una matriz para que el bucle realmente funcione?
Nasser
% x [cmd] .split ( "\ n") devolverá una lista aunque :)
¿Existe alguna documentación sobre cómo realizar pruebas de especificaciones y unidades con Open3 u otros Open en Ruby std-lib? Es difícil probar los shell en mi nivel actual de comprensión.
FilBot3
29
Algunas cosas para pensar al elegir entre estos mecanismos son:
¿Solo quieres stdout o necesitas stderr también? O incluso separado?
¿Qué tan grande es su producción? ¿Quieres guardar todo el resultado en la memoria?
¿Desea leer parte de su salida mientras el subproceso aún se está ejecutando?
¿Necesita códigos de resultado?
¿Necesita un objeto Ruby que represente el proceso y le permita matarlo a pedido?
Es posible que necesite cualquier cosa, desde simples backticks (``) system(), y IO.popenhasta completo Kernel.fork/ Kernel.execcon IO.pipey IO.select.
También puede agregar tiempos de espera en la mezcla si un subproceso tarda demasiado en ejecutarse.
Si realmente necesita Bash, según la nota en la "mejor" respuesta.
Primero, tenga en cuenta que cuando Ruby llama a un shell, generalmente llama /bin/sh, no Bash. Alguna sintaxis de Bash no es compatible /bin/shcon todos los sistemas.
Si necesita usar Bash, inserte bash -c "your Bash-only command"dentro del método de llamada deseado:
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 desea utilizar depende exactamente de lo que está tratando de lograr; Consulte los documentos para obtener más detalles sobre los diferentes métodos.
Usando las respuestas aquí y enlazadas en la respuesta de Mihai, armé una función que cumple con estos requisitos:
Captura perfectamente STDOUT y STDERR para que no se "filtren" cuando mi script se ejecuta desde la consola.
Permite que los argumentos se pasen al shell como una matriz, por lo que no hay necesidad de preocuparse por escapar.
Captura el estado de salida del comando para que quede claro cuando se produjo un error.
Como beneficio adicional, este también devolverá STDOUT en los casos en que el comando de shell salga con éxito (0) y coloque cualquier cosa en STDOUT. De esta manera, difiere de system, lo que simplemente regresa trueen tales casos.
El código sigue. La función específica es system_quietly:
require'open3'classShellError<StandardError;end#actual function:def system_quietly(*cmd)
exit_status=nil
err=nilout=nilOpen3.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.valueendif exit_status.to_i >0
err = err.chomp if err
raiseShellError, err
elsifoutreturnout.chomp
elsereturntrueendend#calling it:begin
puts system_quietly('which','ruby')rescueShellError
abort "Looks like you don't have the `ruby` command. Odd."end#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
No olvide el spawncomando para crear un proceso en segundo plano para ejecutar el comando especificado. Incluso puede esperar a que se complete usando la Processclase y el devuelto pid:
Kernel.spawn()Parece ser mucho más versátil que todas las demás opciones.
Kashyap
6
Si tiene un caso más complejo que el caso común con el que no se puede manejar ``, consulte Kernel.spawn(). Este parece ser el más genérico / con todas las funciones proporcionado por stock Ruby para ejecutar comandos externos.
Puedes usarlo para:
crear grupos de procesos (Windows).
redirigir entrada, salida, error a archivos / entre sí.
establecer env vars, umask.
cambie el directorio antes de ejecutar un comando.
establecer límites de recursos para CPU / datos / etc.
Haga todo lo que se pueda hacer con otras opciones en otras respuestas, pero con más código.
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 =>trueor0: 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.SeeProcess.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,...)ornot:close_others =>false: inherit fds (defaultfor system andexec):close_others =>true: dont inherit (defaultfor spawn and IO.popen)
require'open3'
a="attrib"Open3.popen3(a)do|stdin, stdout, stderr|
puts stdout.read
end
He descubierto que si bien este método no es tan memorable como
system("thecommand")
o
`thecommand`
en backticks, lo bueno de este método en comparación con otros métodos es que los backticks no parecen permitirme putsel comando que ejecuto / guardo el comando que quiero ejecutar en una variable, y system("thecommand")no parece permitirme obtener el resultado mientras que Este método me permite hacer ambas cosas y me permite acceder a stdin, stdout y stderr de forma independiente.
Esto no es realmente una respuesta, pero tal vez alguien lo encuentre útil:
Cuando use TK GUI en Windows, y necesite llamar a comandos de shell desde rubyw, siempre tendrá una ventana CMD molesta que aparecerá por menos de un segundo.
Aquí hay uno genial que uso en un script de ruby en OS X (para que pueda iniciar un script y obtener una actualización incluso después de alejarme de la ventana):
cmd =%Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Open3
( docs ) es la mejor opción para la mayoría de las situaciones, IMO, pero en versiones anteriores de Ruby, no respetará un modificadoPATH
( bugs.ruby-lang.org/issues/8004 ), y dependiendo de cómo pase los argumentos (específicamente , si utiliza hash opts con palabras clave que no son), puede romperse. Pero, si te encuentras con esas situaciones, estás haciendo algo bastante avanzado y puedes descubrir qué hacer leyendo la implementación deOpen3
.Shellwords.escape
( doc ). No querrá insertar la entrada del usuario directamente en los comandos de la shell: ¡escape primero! Ver también comando inyección .Respuestas:
Esta explicación se basa en un script Ruby comentado de 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, generalmente llama
/bin/sh
, no Bash. Alguna sintaxis de Bash no es compatible/bin/sh
con todos los sistemas.Aquí hay formas de ejecutar un script de shell:
Kernel#`
, comúnmente llamados backticks -`cmd`
Esto es como muchos otros lenguajes, incluidos Bash, PHP y Perl.
Devuelve el resultado (es decir, salida estándar) del comando de shell.
Documentos: http://ruby-doc.org/core/Kernel.html#method-i-60
Sintaxis incorporada,
%x( cmd )
Siguiendo al
x
personaje hay un delimitador, que puede ser cualquier personaje. 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 próxima aparición del carácter delimitador.#{ ... }
Se permite la interpolación de cadenas .Devuelve el resultado (es decir, la salida estándar) del comando de shell, al igual que los backticks.
Documentos: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings
Kernel#system
Ejecuta el comando dado en una subshell.
Devuelve
true
si el comando se encontró y se ejecutó correctamente, de lofalse
contrario.Documentos: http://ruby-doc.org/core/Kernel.html#method-i-system
Kernel#exec
Reemplaza el proceso actual ejecutando el comando externo dado.
No devuelve ninguno, el proceso actual se reemplaza y nunca continúa.
Documentos: http://ruby-doc.org/core/Kernel.html#method-i-exec
Aquí hay algunos consejos adicionales:
$?
que es lo mismo que$CHILD_STATUS
, accede al estado del último comando ejecutado por el sistema si usa las teclas de retroceso,system()
o%x{}
. Luego puede acceder a las propiedadesexitstatus
ypid
:Para más lectura ver:
fuente
#{cmd}
y logger.info (#{cmd}
). ¿Hay alguna forma de registrar sus resultados en la producción?cmd
. Si se dan varios argumentos, el comando no se ejecuta con el shell (la misma semántica que Kernel :: exec y Kernel :: system) ".Aquí hay un diagrama de flujo basado en " Cuándo usar cada método para iniciar un subproceso en Ruby ". Vea también, " Engañe a una aplicación para que piense que su stdout es un terminal, no una tubería ".
fuente
Kernel
yProcess
para ser más versátil Es más o menos lo mismo con.PTY.spawn()
, Pero más genérico.La forma en que me gusta hacer esto es usar el
%x
literal, lo que hace que sea fácil (¡y legible!) Usar comillas en un comando, así:Que, en este caso, completará la lista de archivos con todos los archivos de prueba en el directorio actual, que puede procesar como se espera:
fuente
%x[ cmd ]
devuelve una matriz?each' for :String (NoMethodError)
¿cómo funcionó para usted? Estoy usandoruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]
¿Está seguro de que el comando devuelve una matriz para que el bucle realmente funcione?Aquí está el mejor artículo en mi opinión sobre la ejecución de scripts de shell en Ruby: " 6 formas de ejecutar comandos de Shell en Ruby ".
Si solo necesita obtener la salida, utilice las teclas de retroceso.
Necesitaba cosas más avanzadas como STDOUT y STDERR, así que usé la gema Open4. Tienes todos los métodos explicados allí.
fuente
%x
opción de sintaxis.spawn
método cuando encontré esto.Mi favorito es Open3
fuente
stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
Algunas cosas para pensar al elegir entre estos mecanismos son:
Es posible que necesite cualquier cosa, desde simples backticks (``)
system()
, yIO.popen
hasta completoKernel.fork
/Kernel.exec
conIO.pipe
yIO.select
.También puede agregar tiempos de espera en la mezcla si un subproceso tarda demasiado en ejecutarse.
Lamentablemente, depende mucho .
fuente
Una opción mas:
Cuando usted:
Puede usar la redirección de shell:
La
2>&1
sintaxis funciona en Linux , Mac y Windows desde los primeros días de MS-DOS.fuente
Definitivamente no soy un experto en Ruby, pero lo intentaré:
También deberías poder hacer cosas como:
fuente
Las respuestas anteriores ya son bastante buenas, pero realmente quiero compartir el siguiente artículo de resumen: " 6 formas de ejecutar comandos de Shell en Ruby "
Básicamente, nos dice:
Kernel#exec
:system
y$?
:Backticks (`):
IO#popen
:Open3#popen3
- stdlib:Open4#popen4
-- una gema:fuente
Si realmente necesita Bash, según la nota en la "mejor" respuesta.
Si necesita usar Bash, inserte
bash -c "your Bash-only command"
dentro del método de llamada deseado:Probar:
O si está ejecutando un archivo de script existente como
Ruby debería honrar al shebang, pero siempre puedes usar
para asegurarse, aunque puede haber una ligera sobrecarga de
/bin/sh
ejecución/bin/bash
, probablemente no lo note.fuente
También puede usar los operadores de retroceso (`), similares a Perl:
Útil si necesitas algo simple.
El método que desea utilizar depende exactamente de lo que está tratando de lograr; Consulte los documentos para obtener más detalles sobre los diferentes métodos.
fuente
Podemos lograrlo de múltiples maneras.
Usando
Kernel#exec
, nada después de ejecutar este comando:Utilizando
backticks or %x
Usando el
Kernel#system
comando, devuelvetrue
si tiene éxito,false
si no tiene éxito y devuelvenil
si falla la ejecución del comando:fuente
La forma más fácil es, por ejemplo:
fuente
Usando las respuestas aquí y enlazadas en la respuesta de Mihai, armé una función que cumple con estos requisitos:
Como beneficio adicional, este también devolverá STDOUT en los casos en que el comando de shell salga con éxito (0) y coloque cualquier cosa en STDOUT. De esta manera, difiere de
system
, lo que simplemente regresatrue
en tales casos.El código sigue. La función específica es
system_quietly
:fuente
No olvide el
spawn
comando para crear un proceso en segundo plano para ejecutar el comando especificado. Incluso puede esperar a que se complete usando laProcess
clase y el devueltopid
:El documento dice: Este método es similar
#system
pero no espera a que termine el comando.fuente
Kernel.spawn()
Parece ser mucho más versátil que todas las demás opciones.Si tiene un caso más complejo que el caso común con el que no se puede manejar
``
, consulteKernel.spawn()
. Este parece ser el más genérico / con todas las funciones proporcionado por stock Ruby para ejecutar comandos externos.Puedes usarlo para:
La documentación de Ruby tiene suficientes ejemplos:
fuente
El método backticks (`) es el más fácil para llamar a comandos de shell desde Ruby. Devuelve el resultado del comando de shell:
fuente
Dado un comando como
attrib
:He descubierto que si bien este método no es tan memorable como
o
en backticks, lo bueno de este método en comparación con otros métodos es que los backticks no parecen permitirme
puts
el comando que ejecuto / guardo el comando que quiero ejecutar en una variable, ysystem("thecommand")
no parece permitirme obtener el resultado mientras que Este método me permite hacer ambas cosas y me permite acceder a stdin, stdout y stderr de forma independiente.Consulte " Ejecución de comandos en ruby " y la documentación de Ruby's Open3 .
fuente
Esto no es realmente una respuesta, pero tal vez alguien lo encuentre útil:
Cuando use TK GUI en Windows, y necesite llamar a comandos de shell desde rubyw, siempre tendrá una ventana CMD molesta que aparecerá por menos de un segundo.
Para evitar esto, puede usar:
o
Ambos almacenarán la
ipconfig
salida en el interiorlog.txt
, pero no aparecerán ventanas.Necesitará
require 'win32ole'
dentro de su script.system()
,exec()
yspawn()
todos aparecerán esa ventana molesta cuando utilices TK y rubyw.fuente
Aquí hay uno genial que uso en un script de ruby en OS X (para que pueda iniciar un script y obtener una actualización incluso después de alejarme de la ventana):
fuente