2012-03-20 18 views
45

Me gustaría ejecutar un número de comandos bash desde Rakefile. Mi shell activo es bash y llamaré al rake desde bash.Ejecutar comandos bash desde un Rakefile

he incluido en mi Rakefile la siguiente

task :hello do 
    %{echo "World!"} 
end 

pero al momento de celebrar rake hello no hay salida?

¿Cómo ejecuto los comandos bash desde un Rakefile?

NOTA: Esto no es un duplicado, ya que está pidiendo específicamente la forma de ejecutar los comandos de bash de un Rakefile.

+0

No es '% {', es '% x (', y eso devuelve stdout como una cadena en lugar de imprimirlo. –

+7

No es un duplicado como se indicó anteriormente, ya que específicamente solicita la ejecución en un archivo de rake y no en un programa regular de ruby ​​ – Gizmomogwai

+0

¿Cómo es que está marcado como un duplicado cuando pregunta específicamente sobre Rake en lugar de Ruby en general ?! – silverdr

Respuesta

101

I pensar la forma de rastrillo quiere que esto suceda es con: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Ejemplo:

task :test do 
    sh "ls" 
end 

El built-in SH función de rastrillo se ocupa del valor de retorno del comando (la tarea falla si el comando tiene un valor de retorno distinto 0) y además también emite los comandos de salida.

+0

¿Qué quiere decir con la forma en que rake quiere que esto suceda? – rudolph9

+5

Las otras soluciones, como ejecutar con palos de retroceso, etc., no son los métodos de rastrillo, sino los métodos de ruby ​​(ver http://zhangxh.net/programming/ruby/6-ways-to-run-shell-commands-in-ruby/ for 6 diferentes caminos). Lo que hace el rake en la función "sh" es hacerse cargo automáticamente del valor de retorno del comando, de modo que la tarea de rake falla, si falla un comando en sh. La mayoría de las veces esto es justo lo que quieres. Las otras formas de ejecutar comandos desde ruby ​​no deberían usarse con tanta frecuencia en Rake. – Gizmomogwai

+3

otro beneficio de sh over system: sh parece ser más detallado sobre comandos de eco, por defecto. –

55

Existen varias formas de ejecutar comandos de shell en ruby. Un simple (y probablemente el más común) es el uso de acentos abiertos:

task :hello do 
    `echo "World!"` 
end 

invertidas tienen un buen efecto en la salida estándar del comando shell se convierte en el valor de retorno. Así, por ejemplo, se puede obtener la salida de ls haciendo

shell_dir_listing = `ls` 

Pero hay muchas otras maneras de llamar a los comandos de shell y todos ellos tienen ventajas/desventajas y funcionan de forma diferente. This article explica las opciones en detalle, pero aquí hay un rápido posibilidades de resumen:

+0

Esto registra la salida a la consola, lo que puede ser un poco confuso. En rake puedes usar 'sh" some_task "' para tener el comando print to terminal como si lo ejecutaras directamente desde la terminal. – Automatico

3

%{echo "World!"} define una cuerda. Espero que quisieras %x{echo "World!"}.

%x{echo "World!"} ejecuta el comando y devuelve la salida (stdout). No verás el resultado.Pero puede hacerlo:

puts %x{echo "World!"} 

hay más formas de llamar a un comando del sistema:

  • acentos abiertos: `
  • system(cmd)
  • popen
  • Open3#popen3
+1

Cabe señalar que '% x' y backticks llaman al método equivalente de ruby ​​kernel, son sintaxis alternativa para el mismo método exacto. –

+0

'system (cmd)' funcionó para mí. Estaba tratando de escribir un script de generador de pdf usando wkhtmltopdf. – tsega

2

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 Kernel#system para ejecutar comandos de shell. Ruby codifica a cero el /bin/sh, ignorando el shell configurado del usuario o $SHELL en el entorno.

He aquí una solución que invoca fiesta de /bin/sh, lo que le permite seguir utilizando el sh método:

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 tomada de rieles:

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb

Probablemente se podría conseguirlo por requiriendo active_support, o tal vez esté autocargado cuando estás en un proyecto de rieles, pero yo estaba usando esto fuera de rieles y tuve que definirlo yo mismo.

Hay dos heredos, uno externo con los marcadores EOS y uno interior con los marcadores BASH.

La forma en que esto funciona es alimentando el interior heredoc entre los marcadores de BASH a la base de datos 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 rubro 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 /bin/sh al verlo.

/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', indican /bin/sh que no se debe expandir nada dentro del heredoc antes de pasarlo a bash.

Sin embargo, /bin/sh sigue aplicando escapes a la cadena antes de pasarla a bash. Eso significa que los escapes de barra invertida deben duplicarse para pasar el /bin/sh a bash, es decir\ se convierte en \\.

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 canalizació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} argumento 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 desea utilizar ninguna construcción || exit sin establecer explícitamente el código de salida, es decir, || 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 con el pipeline al grep.

+1

Gracias. Esto me ayudó dirigiéndome en la dirección correcta. Lo estoy usando en un script de ruby ​​donde necesito llamar a algunos métodos que necesitan bash. También uso una cadena larga con algunos caracteres "extraños". Así es como lo hago, tal vez pueda ayudar a alguien. 'system (% [xx = $ (cat << EOT \ n # {body_text} \ nEOT \ n); bash << BASH \ n. # {ENV ['HOME']} /. bash_profile; echo" $ xx " | own_mail -TRm \ nBASH \ n)] '. Sin embargo, la manera más fácil sería poner todo en un script bash y simplemente llamar a ese 'system (" path/my_bash_script.sh ")' – 244an

Cuestiones relacionadas