web-dev-qa-db-ja.com

Rakefileからbashコマンドを実行する

bashからいくつかのRakefileコマンドを実行したいと思います。

Rakefileで次のことを試しました

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

しかし、rake helloを実行すると、出力はありませんか? Rakefileからbashコマンドを実行するにはどうすればよいですか?

[〜#〜] note [〜#〜]:これは、Rakefile

64
rudolph9

Rakeでこれを実現する方法は、次のとおりです。 http://rubydoc.info/gems/rake/FileUtils#sh-instance_method 例:

task :test do
  sh "ls"
end

組み込みのrake関数shは、コマンドの戻り値を処理し(コマンドに0以外の戻り値がある場合、タスクは失敗します)、さらにコマンド出力も出力します。

118
Gizmomogwai

Rubyでシェルコマンドを実行する方法はいくつかあります。簡単なもの(そしておそらく最も一般的なもの)は、バックティックを使用することです:

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

バックティックには、シェルコマンドの標準出力が戻り値になるという素晴らしい効果があります。したがって、たとえば、次のようにしてlsの出力を取得できます。

Shell_dir_listing = `ls`

しかし、シェルコマンドを呼び出す方法は他にもたくさんあり、それらにはすべて利点/欠点があり、動作が異なります。 この記事 選択肢の詳細を説明しますが、簡単な要約を以下に示します。

57
Ben Lee

コンセンサスはrakeの_#sh_メソッドを好むように思われますが、OPは明示的にbashを要求するので、この回答には何らかの用途があります。

_Rake#sh_は_Kernel#system_呼び出しを使用してシェルコマンドを実行するため、これは関連性があります。 Rubyは_/bin/sh_にハードコードし、環境内のユーザーの構成済みシェルまたは_$Shell_を無視します。

_/bin/sh_からbashを呼び出す回避策があり、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_はRailsから借用しています:

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

おそらくactive_supportを要求することで取得できます。または、Railsプロジェクトにいるときに自動ロードされますが、私はRails自分で定義します。

2つのヒアドキュメントがあります。マーカーEOSの外側のものと、マーカーBASHの内側のものです。

これが機能する方法は、bashマーカーとbashのstdinの間の内部heredocをフィードすることです。 _/bin/sh_のコンテキスト内で実行されているため、Ruby oneではなくposix heredocであることに注意してください。通常、エンドマーカーは列1にある必要があります。ここでのケースはインデントのためです。

ただし、これはRuby heredocでラップされているため、_strip_heredoc_メソッドはそれを適用し、/bin/sh_それを見て。

_/bin/sh_は通常、heredoc内の変数を展開し、スクリプトを妨害する可能性があります。開始マーカー 'BASH'を囲む単一引用符は、bashに渡される前に、ヒアドキュメント内で何も展開しないように_/bin/sh_に指示します。

ただし、_/bin/sh_は、bashに渡す前に文字列にエスケープを適用します。つまり、バックスラッシュエスケープは、_/bin/sh_を介してbashにするために二重にする必要があります。つまり、_\_は_\\_になります。

Bashオプション_-xeuo pipefail_はオプションです。

_-euo pipefail_引数は、bashに厳密モードで実行するように指示します。これは、パイプライン内のコマンドでさえ、失敗または未定義変数への参照があれば実行を停止します。これにより、rakeにエラーが返され、rakeタスクが停止します。通常、これはあなたが望むものです。通常のbash動作が必要な場合は、引数を削除できます。

Bashの_-x_オプションと_{verbose: false}_の_#sh_引数は連携して機能するため、rakeは実際に実行されるbashコマンドのみを出力します。これは、bashスクリプトが完全に実行されることを意図していない場合、たとえば、スクリプトの早い段階で正常に終了できるテストがある場合に便利です。

Rakeタスクを失敗させたくない場合は、0以外の終了コードを設定しないように注意してください。通常、これは、終了コードを明示的に設定せずに_|| exit_コンストラクトを使用したくない、つまり_|| exit 0_を使用することを意味します。

途中でbashの異常が発生した場合は、_-o pipefail_をオフにします。特にgrepにパイプラインするときに、それに関連するバグを少し見ました。

4
Binary Phile

_%{echo "World!"}_は文字列を定義します。 _%x{echo "World!"}_が欲しいと期待しています。

_%x{echo "World!"}_はコマンドを実行し、出力(stdout)を返します。結果は表示されません。しかし、あなたはそうすることができます:

_puts %x{echo "World!"}
_

システムコマンドを呼び出す方法は他にもあります。

  • バックティック: `
  • system( cmd )
  • popen
  • _Open3#popen3_
3
knut

2つの方法があります。

_sh " expr "
_

または

_%x( expr )
_

(expr)は{expr}、| | expr |または `expr`

違いは、sh "expr"は何かを実行するRubyメソッドであり、%x(expr)はRuby組み込みメソッドです。結果とアクションは異なります。ここに例があります

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

取得する:

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

%x( expr )はShell exprのみを実行し、stdoutは画面に表示されないことがわかります。したがって、コマンドの結果が必要な場合は、use%x( expr )を使用することをお勧めします。

ただし、シェルコマンドを実行するだけの場合は、_sh "expr"_を使用することをお勧めします。 _sh "irb"_はirbシェルに入るので、%x(irb)は無効になるためです。

0
Neo li