Vagrantfile のホスト OS 側で実行するシェル・コマンドにヒアドキュメントを使う

<h1>Vagranfile でローカル実行</h1>

`vagrant up` 時、つまり **Vagrantfile[^vagrantfile] の実行時にホスト OS 側のセットアップをしたい**場合、Ruby の [`system` モジュール関数](https://docs.ruby-lang.org/ja/latest/class/Kernel.html#M_SYSTEM)を使ってシェル・コマンドを実行できます。

しかし、コマンドごとに `system` 関数を使うのが冗長だったので簡素にしたいのです。

```ruby:問題のVagrantfile
# Script to run on Host OS （冗長な書き方。可読性がいささか悪い）
system('rm -rf hello-world > /dev/null 2>&1')
system('echo "Hello world!" > hello-world')

# Script to run on Guest OS （可能なら、上記を下のように描きたい）
$script_guest = <<-'SCRIPT'
  cat /vagrant/hello-world && \
  mv /vagrant/hello-world /vagrant/hello-world.done
SCRIPT

Vagrant.configure("2") do |config|
  config.vm.box = "KEINOS/macOS.10.14.1_Japanese"
  config.vm.network :private_network, ip: "192.168.1.100"
  config.vm.synced_folder ".", "/vagrant", type: "nfs"
  config.vm.provision "shell", inline: $script_guest, privileged: false, run: "always"

  config.vm.provider "virtualbox" do |vb|
    （...略...）
  end
end
```

他の言語同様に[ヒアドキュメント](https://www.google.com/search?q=site:qiita.com+ruby+%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88)を使うのが定石だと思うのですが、変数に代入してからでなく、**直接 `system` 関数の引数にヒアドキュメントで複数行渡したい**のです。

## TL; DR <sub><sup><sub><sup>（今北産業）</sup></sub></sup></sub>

> Ruby で system 関数の引数にヒアドキュメントで複数行コマンドを渡す

```ruby
system(<<~'EOD')
  rm -rf hello-world > /dev/null 2>&1
  echo "Hello world!" > hello-world
EOD
```

- 参考文献
  - 「[Ruby｜複数のヒアドキュメントをメソッドの引数にする](https://qiita.com/guppy0356/items/c5de6938b67b24772864)」@ Qiita
  - [当記事のコメント](#comment-d92cdee6ac833cf3e6da) by @scivola

## TS; DR <sub><sup><sub><sup>ヒアドキュメントに悩んだコマケーこと</sup></sub></sup></sub>

### Vagrantfile って Ruby だもの

最初は、Bash や PHP などの感覚で以下のようにしていました。

```ruby:他の言語でもありがちな書き方
system(<<-'EOD'
  rm -rf hello-world > /dev/null 2>&1
  echo "Hello world!" > hello-world
EOD
)
```

直感で書いたのですが動いたので、これで良いと言えば良いのですが、そもそも Ruby はよくわかりません。

いささか気持ちが悪かったので、ちょっと調べてみたところ、[Qiita 記事のコメント](https://qiita.com/FumiyaShibusawa/items/ae5c851517a5b5690305#comment-fc5f539942baa0add3e9)から、以下のように書けることを知りました。

```ruby:Rubyらしい書き方
system(<<-'EOD')
  rm -rf hello-world > /dev/null 2>&1
  echo "Hello world!" > hello-world
EOD
```

変数の場合のヒアドキュメントに近い状態になり、スッキリしました。PHP などのヒアドキュメントと違うんですね。面白い。

また、[この記事のコメント](#comment-d92cdee6ac833cf3e6da)にもいただいたように、ヒアドキュメント内の行頭のインデントを削除してくれる記法もありました。`<<-` を `<<~` （ハイフンをチルダ）にすると余計なインデントを削除してくれます。便利。

```ruby:Rubyらしい書き方（インデント削除）
system(<<~'EOD')
  # remove old file
    rm -rf hello-world > /dev/null 2>&1
  # create new file
    echo "Hello world!" > hello-world
EOD
```

上記は以下の文字列として `system` に渡されます。一番浅いインデントを基準に行頭がトリムされます。

```bash
# remove old file
  rm -rf hello-world > /dev/null 2>&1
# create new file
  echo "Hello world!" > hello-world
```


最終的には以下のようになりました。

```ruby:やりたかった形のVagrantfile
# Script to run on Host OS
system(<<~'SCRIPT')
  rm -rf hello-world > /dev/null 2>&1
  echo "Hello world!" > hello-world
SCRIPT

# Script to run on Guest OS
$script_guest = <<~'SCRIPT'
  cat /vagrant/hello-world && \
  mv /vagrant/hello-world /vagrant/hello-world.done
SCRIPT

Vagrant.configure("2") do |config|
  config.vm.box = "KEINOS/macOS.10.14.1_Japanese"
  config.vm.network :private_network, ip: "192.168.1.100"
  config.vm.synced_folder ".", "/vagrant", type: "nfs"
  config.vm.provision "shell", inline: $script_guest, privileged: false, run: "always"

  config.vm.provider "virtualbox" do |vb|
    （...略...）
  end
end
```

## 参考文献

- 「[Ruby｜複数のヒアドキュメントをメソッドの引数にする](https://qiita.com/guppy0356/items/c5de6938b67b24772864)」@ Qiita
- 「[Rubyのヒアドキュメントの書き方いろいろ](https://blog.toshimaru.net/ruby-heredoc/)」@ blog.toshimaru.net
- 「[Ruby におけるヒアドキュメント（行指向文字列リテラル）の基本的な仕様](https://qiita.com/FumiyaShibusawa/items/ae5c851517a5b5690305)」@ Qiita

[^vagrantfile]: 【Vagrantfile とは】[Vagrant](https://www.vagrantup.com/) が使う設定ファイルのこと。Vagrant は、プロバイダーと呼ばれる VirtualBox / VMware / Docker などの**仮想マシン（VM）系のアプリをコマンドで操作するためのツール**です。各々のアプリ（プロバイダ）もコマンド操作は可能ですが、Vagrant を通すと共通のコマンドで操作できるようになります。Vagrant は Ruby で作成されているため、設定ファイルも Ruby で記載します。「[Vagrant](https://ja.wikipedia.org/wiki/Vagrant_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2))」@ Wikipedia