bash
からいくつかのRakefile
コマンドを実行したいと思います。
Rakefile
で次のことを試しました
task :hello do
%{echo "World!"}
end
しかし、rake hello
を実行すると、出力はありませんか? Rakefileからbashコマンドを実行するにはどうすればよいですか?
[〜#〜] note [〜#〜]:これは、Rakefile。
Rakeでこれを実現する方法は、次のとおりです。 http://rubydoc.info/gems/rake/FileUtils#sh-instance_method 例:
task :test do
sh "ls"
end
組み込みのrake関数shは、コマンドの戻り値を処理し(コマンドに0以外の戻り値がある場合、タスクは失敗します)、さらにコマンド出力も出力します。
Rubyでシェルコマンドを実行する方法はいくつかあります。簡単なもの(そしておそらく最も一般的なもの)は、バックティックを使用することです:
task :hello do
`echo "World!"`
end
バックティックには、シェルコマンドの標準出力が戻り値になるという素晴らしい効果があります。したがって、たとえば、次のようにしてls
の出力を取得できます。
Shell_dir_listing = `ls`
しかし、シェルコマンドを呼び出す方法は他にもたくさんあり、それらにはすべて利点/欠点があり、動作が異なります。 この記事 選択肢の詳細を説明しますが、簡単な要約を以下に示します。
stdout =%x {cmd} -バックティックの代替構文、バックグラウンドで同じことをしている
exec(cmd) -実行中のプロセスを新しいcmdプロセスに完全に置き換えます
success = system(cmd) -サブプロセスを実行し、成功/失敗時にtrue/falseを返します(cmd終了ステータスに基づく)
IO#popen(cmd){| io |} -サブプロセスを実行し、stdoutとstderrをioに接続します
stdin、stdout、stderr = Open3.popen3(cmd) -サブプロセスを実行し、すべてのパイプに接続します(in、out、err)
コンセンサスは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
にパイプラインするときに、それに関連するバグを少し見ました。
_%{echo "World!"}
_は文字列を定義します。 _%x{echo "World!"}
_が欲しいと期待しています。
_%x{echo "World!"}
_はコマンドを実行し、出力(stdout)を返します。結果は表示されません。しかし、あなたはそうすることができます:
_puts %x{echo "World!"}
_
システムコマンドを呼び出す方法は他にもあります。
system( cmd )
popen
Open3#popen3
_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)
は無効になるためです。