$ bundle exec rails s
bundle exec
ってなんぞ?
前提
- rbenv
- ruby 2.3.7
結論
rbenvが下準備を整えてから、bundler gem の bin にあるbundle
を実行。
その bundle
が引数のコマンドを exec
する!
(完全な理解はしてない。)
Outline
- bundle
- exec
- 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(実行)してる。(と予想)
シェルの守備範囲を抜けたということで今回は一旦ここまで。