Rakeのshで外部コマンドを実行する

`require 'rake'`すると[sh](http://docs.seattlerb.org/rake/FileUtils.html#method-i-sh)というメソッドが定義される。内部では[Kernel.system](http://rurema.clear-code.com/2.2.0/method/Kernel/m/system.html)が使われており、Kernel.systemとよく似た振る舞いをするが、いくつかの機能が追加されている。ここではそれらの機能について解説する。

```basic.rb
require 'rake'

sh "ruby -e 'puts :stdout; warn :stderr;'"
```

```basic.rbの実行結果
ruby -e 'puts :stdout; warn :stderr;'
stdout
stderr
```

## 実行に失敗するとデフォルトでRuntimeErrorが発生する

```exception.rb
require 'rake'

sh 'ruby -e "exit 1"'
```

```exception.rbの実行結果
ruby -e "exit 1"
/home/vzvu3k6k/.anyenv/envs/rbenv/versions/2.2.0/lib/ruby/2.2.0/rake/file_utils.rb:66:in `block in create_shell_runner': Command failed with status (1): [ruby -e "fail"...] (RuntimeError)
（後略）
```

[Kernel.system](http://rurema.clear-code.com/2.2.0/method/Kernel/m/system.html)や[`](http://rurema.clear-code.com/2.2.0/method/Kernel/m/=60.html)と違い、コマンドが失敗した場合はRuntimeErrorが発生する。つまり、失敗時には勝手にタスクの実行が止まる。これによって、途中のステップが失敗したまま次の処理が実行されて意図しない結果になったり、処理が失敗したことにさえ気づかないといったトラブルを防ぐことができる。

## ブロックで実行結果を受け取る

```block.rb
require 'rake'

sh "ruby -e 'exit 1'" do |ok, status|
  p({ok: ok, status: status})
end

puts "例外は発生しない"
```

```block.rbの実行結果
ruby -e 'exit 1'
{:ok=>false, :status=>#<Process::Status: pid 27598 exit 1>}
例外は発生しない
```

ブロックを渡すと、コマンドの実行結果（true/false）、コマンドの実行ステータス（[$?](http://rurema.clear-code.com/2.2.0/method/Kernel/v/=3f.html)）を引数にして呼び出される。ブロックが渡された場合には、コマンドが失敗してもRuntimeErrorが発生しない。

## オプション

引数の末尾にHashを付けることで、verbose, noopオプションを設定できる。

参考: [Ruby リファレンスマニュアル - module FileUtils - オプションの説明](http://rurema.clear-code.com/2.2.0/class/FileUtils.html#options)

### verbose

verboseが偽値だと実行するコマンドが表示されなくなる。

```verbose.rb
require 'rake'

puts "# default"
sh "echo foobar"

puts

puts "# verbose: false"
sh "echo foobar", verbose: false
```

```verbose.rbの実行結果
# default
echo foobar
foobar

# verbose: false
foobar
```

### noop

noopが偽値だとコマンドが実行されなくなる。

```noop.rb
require 'rake'

sh "touch dummy", noop: true # コマンドが実行されない
puts File.exist? "dummy" # ファイルが作られていないのでfalseになる
```

verboseとnoopを同時に設定することもできる。

### verboseとnoopのデフォルト値を設定する

verboseのデフォルト値は[verbose](http://docs.seattlerb.org/rake/Rake/FileUtilsExt.html#method-i-verbose)メソッドで、noopのデフォルト値は[nowrite](http://docs.seattlerb.org/rake/Rake/FileUtilsExt.html#method-i-nowrite)メソッドで設定できる。

この設定値はshだけではなく、FileUtilsの他のメソッドにも適用される。

```verbose.rb
require 'rake'

verbose(false)
sh "表示されない" rescue nil

verbose(true) do
  sh "verboseは#{verbose}になり、表示される" rescue nil
end

sh "ブロックを抜けるとverboseは元の状態に戻るので表示されない" rescue nil
sh "オプションを直接指定すると上書き可能", verbose: true rescue nil
```

### Kernel.systemにオプションを渡す

Kernel.systemにオプションを渡すこともできる。引数の末尾のHashはverbose, noopのオプションと解釈されることに注意。

```system_options.rb
require 'rake'

begin
  sh "pwd", {chdir: "/"}
rescue
  # 末尾のHashはsystemへのオプションではなく
  # verbose, noopのオプションと解釈されるので、
  # <ArgumentError: no such option: chdir>が発生する。
  p $!
end

# systemにオプションを渡すためには
# 常に末尾にverbose, noopオプションのためのHashを付けないといけない。
sh "pwd", {chdir: "/"}, {}
```

## そのほか

RakeはRubyを起動する[ruby](http://docs.seattlerb.org/rake/FileUtils.html#method-i-ruby)というメソッドも提供している。動作はshに準ずる。

Rubyで外部コマンドを実行する方法については下記を参照。

* [5 ways to run commands from Ruby | mentalized](http://mentalized.net/journal/2010/03/08/5_ways_to_run_commands_from_ruby/)
* [Rubyで外部コマンドを実行して結果を受け取る方法あれこれ - Qiita](http://qiita.com/tyabe/items/56c9fa81ca89088c5627)

<a rel="license" href="http://creativecommons.org/publicdomain/zero/1.0/"><img src="http://i.creativecommons.org/p/zero/1.0/80x15.png" style="border-style: none;" alt="CC0" /></a>