LoginSignup
7
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-03-22

Vagranfile でローカル実行

vagrant up 時、つまり Vagrantfile1 の実行時にホスト OS 側のセットアップをしたい場合、Ruby の system モジュール関数を使ってシェル・コマンドを実行できます。

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

問題の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

他の言語同様にヒアドキュメントを使うのが定石だと思うのですが、変数に代入してからでなく、直接 system 関数の引数にヒアドキュメントで複数行渡したいのです。

TL; DR (今北産業)

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

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

TS; DR ヒアドキュメントに悩んだコマケーこと

Vagrantfile って Ruby だもの

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

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

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

いささか気持ちが悪かったので、ちょっと調べてみたところ、Qiita 記事のコメントから、以下のように書けることを知りました。

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

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

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

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

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

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

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

やりたかった形の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

参考文献


  1. 【Vagrantfile とは】Vagrant が使う設定ファイルのこと。Vagrant は、プロバイダーと呼ばれる VirtualBox / VMware / Docker などの仮想マシン(VM)系のアプリをコマンドで操作するためのツールです。各々のアプリ(プロバイダ)もコマンド操作は可能ですが、Vagrant を通すと共通のコマンドで操作できるようになります。Vagrant は Ruby で作成されているため、設定ファイルも Ruby で記載します。「Vagrant」@ Wikipedia 

7
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2