bundle exec を理解したい。

`$ bundle exec rails s`

`bundle exec` ってなんぞ？

## 前提
- rbenv
- ruby 2.3.7

## 結論
rbenvが下準備を整えてから、bundler gem の bin にある`bundle` を実行。
その `bundle` が引数のコマンドを `exec` する！
(完全な理解はしてない。)

## Outline
1. bundle
2. exec
3. bundle exec

## bundle
これはシェルのコマンド。シェルのコマンドってなんぞ？
組込コマンド or `$PATH` にいるファイルの名前（厳密には違うだろう <-）
`bundle` は後者であると予想されるため、中身を見てみる。

```
% which echo
echo: shell built-in command
% which bundle
/Users/nishigami.ryosuke/.rbenv/shims/bundle
% cat `which bundle`
#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"
if [ "$program" = "ruby" ]; then
  for arg; do
    case "$arg" in
    -e* | -- ) break ;;
    */* )
      if [ -f "$arg" ]; then
        export RBENV_DIR="${arg%/*}"
        break
      fi
      ;;
    esac
  done
fi

export RBENV_ROOT="/Users/nishigami.ryosuke/.rbenv"
exec "/Users/nishigami.ryosuke/.rbenv/libexec/rbenv" exec "$program" "$@"
```

注目すべきは最後の行。これが `bundle` のメイン処理とにらみをつける。
`exec "/Users/nishigami.ryosuke/.rbenv/libexec/rbenv" exec "$program" "$@"`

つまりこの、`exec` のシンタックスを理解しなければ自分は次のステージに進めない。。
`exec hogehoge exec hoge $@`

## exec

```
% which exec
exec: shell built-in command
% help exec
exec [ -cl ] [ -a argv0 ] [ command [ arg ... ] ]
       Replace  the current shell with command rather than forking.  If
       command is a shell builtin command  or  a  shell  function,  the
       shell executes it, then immediately exits.

       ...
```

ほう。
とりあえず試してみる。

```
% exec echo hoge
hoge

[プロセスが完了しました]
```

なるほど。

> Replace the current shell with command rather than forking.
> then immediately exits.

多分zshのプロセスが `echo` に置き換えられ、`exit` した。
子プロセスが `exit` した訳ではないのでこうなる。
気を取り直してもう一回。

```
% exec echo hoge | cat
hoge
% exec ruby -e 'puts %i[hoge fuga]' | cat
hoge
fuga
```

`exec` はただ引数のコマンドを今のプロセスで実行してexitするもの(らしい)という理解を得た！

## bundle exec
`exec "/Users/nishigami.ryosuke/.rbenv/libexec/rbenv" exec "$program" "$@"`

これを解いてゆく。

`bundle exec rails s`
の場合上記はこうなる。

`exec "/Users/nishigami.ryosuke/.rbenv/libexec/rbenv" exec "bundle" "exec rails s"`

`exec` サンドイッチ祭りだ。

```
% cat /Users/nishigami.ryosuke/.rbenv/libexec/rbenv
...

command="$1"
case "$command" in

...

* )
  command_path="$(command -v "rbenv-$command" || true)"

  ...

  shift 1
  if [ "$1" = --help ]; then
    if [[ "$command" == "sh-"* ]]; then
      echo "rbenv help \"$command\""
    else
      exec rbenv-help "$command"
    fi
  else
    exec "$command_path" "$@"
  fi
  ;;
esac
```

`/Users/nishigami.ryosuke/.rbenv/libexec/rbenv-exec` が実行される。
つまりこうゆう状況。
`exec /Users/nishigami.ryosuke/.rbenv/libexec/rbenv-exec bundle exec rails s`

この辺りで気づく。自分が `rbenv` を使っているので `bundle` は `rbenv` 下でいい感じに実行されることに。
そしてこの記事が `rbenv` の場合に限定された話に絞られていたことに。。
構わず続ける。

```
% cat /Users/nishigami.ryosuke/.rbenv/libexec/rbenv-exec
...

RBENV_COMMAND="$1"

...

RBENV_COMMAND_PATH="$(rbenv-which "$RBENV_COMMAND")"

...

shift 1

...

exec -a "$RBENV_COMMAND" "$RBENV_COMMAND_PATH" "$@"
```
最後の行はこう。

`exec -a bundle /Users/nishigami.ryosuke/.rbenv/versions/2.3.7/bin/bundle exec rails s`

```
% cat /Users/nishigami.ryosuke/.rbenv/versions/2.3.7/bin/bundle
#!/Users/nishigami.ryosuke/.rbenv/versions/2.3.7/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'bundler' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

if ARGV.first
  str = ARGV.first
  str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
  if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
    version = $1
    ARGV.shift
  end
end

load Gem.bin_path('bundler', 'bundle', version)
```

これはほぼこう

```
#!/Users/nishigami.ryosuke/.rbenv/versions/2.3.7/bin/ruby

require 'rubygems'

load Gem.bin_path('bundler', 'bundle', version)
```

一行目をご覧の通り、シェルの守備範囲を抜けていつの間にかrubyになっている！！
bundler gemの bundle ってbinをload(実行)してる。(と予想)
シェルの守備範囲を抜けたということで今回は一旦ここまで。