web-dev-qa-db-fra.com

Exécuter des commandes bash à partir d'un rakefile

Je voudrais exécuter un certain nombre de commandes bash à partir d'un Rakefile.

J'ai essayé ce qui suit dans mon Rakefile

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

mais lors de l'exécution de rake hello il n'y a pas de sortie? Comment exécuter des commandes bash à partir d'un rakefile?

NOTE : Ce n'est pas un doublon car il demande spécifiquement comment exécuter les commandes bash à partir d'un Rakefile .

64
rudolph9

Je pense que la façon dont Rake veut que cela se produise est avec: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Exemple:

task :test do
  sh "ls"
end

La fonction de râteau intégrée sh prend en charge la valeur de retour de la commande (la tâche échoue si la commande a une valeur de retour autre que 0) et en plus elle génère également la sortie des commandes.

118
Gizmomogwai

Il existe plusieurs façons d'exécuter des commandes Shell dans Ruby. Un simple (et probablement le plus courant) consiste à utiliser des backticks:

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

Les backticks ont un bel effet où la sortie standard de la commande Shell devient la valeur de retour. Ainsi, par exemple, vous pouvez obtenir la sortie de ls en faisant

Shell_dir_listing = `ls`

Mais il existe de nombreuses autres façons d'appeler des commandes Shell et elles ont toutes des avantages/inconvénients et fonctionnent différemment. Cet article explique les choix en détail, mais voici un bref résumé des possibilités:

57
Ben Lee

Étant donné que le consensus semble préférer le rake #sh, mais OP demande explicitement bash, cette réponse peut avoir une certaine utilité.

Ceci est pertinent puisque Rake#sh utilise le Kernel#system appel pour exécuter les commandes Shell. Ruby hardcodes que pour /bin/sh, en ignorant le shell configuré par l'utilisateur ou $Shell dans l'environnement.

Voici une solution de contournement qui appelle bash à partir de /bin/sh, vous permettant de continuer à utiliser la méthode 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 est emprunté à Rails:

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

Vous pourriez probablement l'obtenir en exigeant active_support, ou peut-être qu'il est chargé automatiquement lorsque vous êtes dans un projet Rails, mais j'utilisais cela en dehors Rails et ainsi de suite) pour le défendre moi-même.

Il existe deux heredocs, une externe avec les marqueurs EOS et une interne avec les marqueurs BASH.

La façon dont cela fonctionne consiste à alimenter l'hérédoc intérieur entre les marqueurs BASH à la stdin de bash. Notez qu'il s'exécute dans le contexte de /bin/sh, c'est donc un heredoc posix pas un Ruby one. Normalement, cela nécessite que le marqueur de fin soit dans la colonne 1, ce qui n'est pas le cas ici à cause de l'indentation.

Cependant, comme il est enveloppé dans un Ruby heredoc, le strip_heredoc la méthode qui y est appliquée la désindente, en plaçant l'intégralité du côté gauche de l'hérédoc intérieur dans la colonne 1 avant /bin/sh le voyant.

/bin/sh étendrait normalement les variables dans l'hérédoc, ce qui pourrait interférer avec le script. Les guillemets simples autour du marqueur de début, 'BASH', indiquent /bin/sh pour ne rien développer à l'intérieur de l'hérédoc avant qu'il ne soit passé à bash.

Toutefois /bin/sh applique toujours des échappements à la chaîne avant de la passer à bash. Cela signifie que les échappements antislash doivent être doublés pour passer par /bin/sh à bash, c'est-à-dire \ devient \\.

Les options bash -xeuo pipefail sont facultatifs.

Le -euo pipefail les arguments indiquent à bash de s'exécuter en mode strict, ce qui arrête l'exécution en cas d'échec ou de référence à une variable non définie, même une commande dans un pipeline. Cela renverra une erreur au râteau, ce qui arrêtera la tâche de râteau. C'est généralement ce que vous voulez. Les arguments peuvent être supprimés si vous souhaitez un comportement bash normal.

Le -x option pour bash et {verbose: false} argument à #sh fonctionne de concert afin que rake affiche uniquement les commandes bash qui sont réellement exécutées. Ceci est utile si votre script bash n'est pas destiné à s'exécuter dans son intégralité, par exemple s'il a un test qui lui permet de se terminer correctement au début du script.

Veillez à ne pas définir un code de sortie autre que 0 si vous ne souhaitez pas que la tâche de râteau échoue. Cela signifie généralement que vous ne voulez pas utiliser de || exit construit sans définir explicitement le code de sortie, c'est-à-dire || exit 0.

Si vous rencontrez des bizarreries bash en cours de route, désactivez le -o pipefail. J'ai vu un peu de bug lié à cela spécifiquement lors du pipelining vers grep.

4
Binary Phile

%{echo "World!"} Définit une chaîne. Je suppose que vous vouliez %x{echo "World!"}.

%x{echo "World!"} Exécute la commande et renvoie la sortie (stdout). Vous ne verrez pas le résultat. Mais vous pouvez faire:

puts %x{echo "World!"}

Il existe plusieurs façons d'appeler une commande système:

  • Backticks: `
  • system( cmd )
  • popen
  • Open3#popen3
3
knut

Il y a deux façons:

sh " expr "

ou

%x( expr )

Gardez à l'esprit que (expr) peut être {expr}, | expr | ou `expr '

La différence est que sh "expr" est une méthode Ruby pour exécuter quelque chose, et% x (expr) est la méthode intégrée Ruby. Le résultat et l'action sont différentes. Voici un exemple

task :default do
value = sh "echo hello"
puts value
value = %x(echo world)
puts value
end

obtenir:

hello  # from sh "echo hello"
true   # from puts value
world  # from puts value

Vous pouvez voir que %x( expr ) ne fera que l'expr Shell, mais la sortie standard ne s'affichera pas à l'écran. Donc, vous feriez mieux d'utiliser%x( expr ) lorsque vous avez besoin du résultat de la commande.

Mais si vous voulez juste faire une commande Shell, je vous recommande d'utiliser sh "expr". Parce que sh "irb" Vous fera entrer dans le shell irb, tandis que %x(irb) sera mort.

0
Neo li