ruby - Ejecute los comandos bash desde un Rakefile
(4)
Esta pregunta ya tiene una respuesta aquí:
- Llamar comandos de shell de Ruby 20 respuestas
Me gustaría ejecutar una serie de comandos bash
desde un Rakefile
. Mi shell activo es bash
y rake
a rake
desde bash
.
He incluido en mi Rakefile
lo siguiente
task :hello do
%{echo "World!"}
end
pero al ejecutar rake hello
no hay salida?
¿Cómo ejecuto los comandos bash desde un Rakefile?
NOTA : Esto no es un duplicado ya que específicamente pregunta cómo ejecutar comandos bash desde un Rakefile .
Creo que la manera en que Rake quiere que esto suceda es con: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Ejemplo:
task :test do
sh "ls"
end
La función de rastreo integrada sh se ocupa del valor de retorno del comando (la tarea falla si el comando tiene un valor de retorno distinto de 0) y además también emite los comandos de salida.
Dado que el consenso parece preferir el método #sh
de rake, pero OP solicita explícitamente bash, esta respuesta puede ser útil.
Esto es relevante ya que Rake#sh
usa la llamada al Kernel#system
para ejecutar comandos de shell. Ruby codifica a ese /bin/sh
, ignorando el shell configurado por el usuario o $SHELL
en el entorno.
Aquí hay una solución alternativa que invoca bash desde /bin/sh
, lo que le permite seguir utilizando el método sh
:
task :hello_world do
sh <<-EOS.strip_heredoc, {verbose: false}
/bin/bash -xeuo pipefail <<''BASH''
echo "Hello, world!"
BASH
EOS
end
class String
def strip_heredoc
gsub(/^#{scan(/^[ /t]*(?=/S)/).min}/, ''''.freeze)
end
end
#strip_heredoc
es prestado de los rieles:
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb
Probablemente puedas obtenerlo requiriendo active_support, o tal vez esté autocargado cuando estés en un proyecto de rieles, pero yo estaba usando este fuera de rieles y tuve que definirlo yo mismo.
Hay dos heredocs, uno externo con los marcadores EOS
y uno interno con los marcadores BASH
.
La forma en que esto funciona es alimentando el inside heredoc entre los marcadores de BASH para el stdin de bash. Tenga en cuenta que se está ejecutando dentro del contexto de /bin/sh
, por lo que es un posix heredoc no uno de ruby. Normalmente eso requiere que el marcador final esté en la columna 1, que no es el caso aquí debido a la sangría.
Sin embargo, como está envuelto dentro de un strip_heredoc
heredoc, el método strip_heredoc
aplicado allí lo deinyecta, colocando la totalidad del lado izquierdo del heredoc interno en la columna 1 antes de que /bin/sh
vea.
/bin/sh
también normalmente expandiría variables dentro del heredoc, lo que podría interferir con el script. Las comillas simples alrededor del marcador de inicio, ''BASH'', le indican a /bin/sh
no expanda nada dentro del heredoc antes de pasarlo a bash.
Sin embargo, /bin/sh
todavía aplica escapes a la cadena antes de pasarlo a bash. Eso significa que los escapes de barra invertida tienen que duplicarse para pasar de /bin/sh
a bash, es decir, /
vuelve //
.
Las opciones de bash -xeuo pipefail
son opcionales.
Los argumentos -euo pipefail
indican a bash que se ejecute en modo estricto, lo que detiene la ejecución ante cualquier falla o referencia a una variable indefinida, incluso un comando en una interconexión. Esto devolverá un error al rake, lo que detendrá la tarea de rake. Usualmente esto es lo que quieres. Los argumentos se pueden descartar si quieres un comportamiento bash normal.
La opción -x
para bash y {verbose: false}
para #sh
funcionan en concierto para que rake solo imprima los comandos bash que se ejecutan realmente. Esto es útil si su script bash no está destinado a ejecutarse en su totalidad, por ejemplo, si tiene una prueba que le permite salir graciosamente al principio del script.
Tenga cuidado de no establecer un código de salida que no sea 0 si no desea que la tarea de rake falle. Por lo general, eso significa que no quieres usar ningún || exit
|| exit
construcciones sin establecer el código de salida explícitamente, es decir || exit 0
|| exit 0
Si te encuentras con alguna rareza de bash en el camino, apaga el -o pipefail
. He visto un poco de bugginess relacionado específicamente cuando se conecta a grep
.
Hay varias formas de ejecutar comandos de shell en ruby. Una simple (y probablemente la más común) es usar backticks:
task :hello do
`echo "World!"`
end
Los Backticks tienen un efecto agradable donde la salida estándar del comando de shell se convierte en el valor de retorno. Entonces, por ejemplo, puedes obtener la salida de ls
haciendo esto
shell_dir_listing = `ls`
Pero hay muchas otras formas de llamar comandos de shell y todos tienen ventajas / inconvenientes y funcionan de manera diferente. Este artículo explica las opciones en detalle, pero aquí hay unas posibilidades de resumen rápido:
stdout =% x {cmd} - Sintaxis alternativa para backticks, detrás de escena está haciendo lo mismo
exec(cmd) : reemplace completamente el proceso en ejecución con un nuevo proceso cmd
success = system (cmd) - Ejecute un subproceso y devuelva verdadero / falso en caso de éxito / falla (según el estado de salida de cmd)
IO # popen (cmd) {| io | } - Ejecute un subproceso y conecte stdout y stderr a io
stdin, stdout, stderr = Open3.popen3 (cmd) - Ejecuta un subproceso y conéctate a todos los pipes (in, out, err)
%{echo "World!"}
define una Cadena. Supongo que querías %x{echo "World!"}
.
%x{echo "World!"}
ejecuta el comando y devuelve la salida (stdout). No verás el resultado. Pero puedes hacer:
puts %x{echo "World!"}
Hay más formas de llamar a un comando del sistema:
- Backticks: `
-
system( cmd )
-
popen
-
Open3#popen3