ターミナルの起動が遅い。
最近、妙にターミナルの起動が遅くて苦痛だなと思って調べたら pyenv 関連だった。
rbenv もそうだったし、pyenv もやはり遅い。 rbenv に関してはrbenv init では諦めて、symlink を使って解決しているが pyenv は別の方法を模索した。
pyenv 関連の起動遅いねん

ああ、わりと時間かかりすぎ。
## init 系を測定してみる
time {
## rb
eval "$(rbenv init -)"
## py
eval "$(pyenv init -)"
eval "$(pip completion --bash)"
eval "$(pipenv --completion)"
}
シェル起動の処理で4秒超えは、ストレス溜まりそう。

ターミナルの起動を早くしたい。
python は使わないこともあるし、ターミナル自体はもっと早く起動してサクサクと使いたい。
lazyload をする方法が紹介されている
ログインシェルの起動を高速化する lazyenv を作った - Qiita
方針としては
- pyenv と同名の function pyenv(){} をbashrcに登録する
- pyenv 関数内で、自分自身を消す
- 消すときに同時にpyenv をちゃんとロードする
- 自分自身のかわりにロードしたpyenv を起動する。
これらの関数をpyenv の初期化に変わって実行すると、bashrc 内部で init が行われないので、bashrcの読み込みが、遅くなることはない。
シェルスクリプトで表すとこんな感じ。
pyenv () {
unset -f pyenv
pyenv_load_function
pyenv "$@"
}
上記の関数は、関数名とロード関数名が同じであれば、抽象化できるので、作ろうと思ったら、すでに作ってる人がいた。
使い方
私は、遅延ロードさせる関数を .bashrc.d/ に配置した。
.bashrc
bashrc.d をロードする。
if [[ -d ~/.bashrc.d/ ]]; then for i in ~/.bashrc.d/* ; do source $i; done fi
以下のファイル名にして、layzyenv を先に読み込まれるように設定。
.bashrc.d/00-lazyenv.bash
これは、takezoh/lazyenv から取得した。
.bashrc.d/pyenv-init.bash
## 2019-04-13 takuya
## python 関連の設定をまとめる
## lazyload を使って pip を遅延させる
# これをすることでコマンドを実行するまで補完ができなくなるので
# 初回起動まで、補完が出来なかったりする
# なので、一見すると補完が効かないように見えることがあるので注意
## pyenv init -
## pip completion
## pipenv --completion
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export PYTHONSTARTUP=~/.pythonrc
_pyenv_init() {
# python 関連の起動遅いから、ここにまとめる
# pyenv を使う
if ( type "$0" > /dev/null 2>&1 || [[ -d ~/.pyenv ]] ) && [[ $TERM != screen* ]]; then
### pyenv
eval "$(pyenv init -)"
eval "$(pip completion --bash)"
eval "$(pipenv --completion)"
# cmd_is_exists pyenv-virtualenv && eval "$(pyenv virtualenv-init -)"
fi
}
eval "$(lazyenv.load _pyenv_init pyenv python pip pipenv )"
screen / tmux の起動時は環境変数を引き継ぐので重複処理はしないようにしてたり。
他の似たようなコマンドはどうしたか
nvm / node
これらに関しては、bashrcで読み込む必要もないし、プロジェクト単位で使うことが前提でなので、bashrc から外した。 その後 nvm はあまり使わないことに気づいたのでnvm 自体を外した。
rbenv /ruby
これらに関しては、私が個人的に結構ruby使うのと、brew がruby を使ってることもあり、rbenv 自体を外すことは諦めた。それぞれ固定バージョンへのsymlinkを作成してPATHの優先度で対応した。 rbenv init は使わずに済ませた。
phpenv / php
これらに関しては、プロジェクト単位にbashrc を作ることで対応している。
その他の解決方法。
pyenv 自体は、バージョンとコマンド名を解決するものなので、bash 用に専用の pyenv-virtualenv を作り、symlinkしてバージョンを固定。 シェルではロードして利用し、プロジェクトや環境ではプロジェクト環境ごとに pyenv を毎回ロードして使ってもいいかもしれない。
処理後の時間
1秒未満なら、まぁ許容範囲かな。500ms 切りたいとこではあるが。brew の completion 系がたくさんあるのでそれを外すのは難しいし。諦めた。

2019/05/17 追記
pyenv init を見ていて気づいた。
pyenv init を実行してPATH を見ようと実行して気づいたのですが。
pyenv init 自体は、pyenv の別名関数を定義していて、pyenv は遅延ロード的な動作になっている。
他の処理を取り除いた bash や root の bash で pyenv init - するとすごく早い。
もしかしたらpyenv init が遅い原因は、pyenv ではなく、brew など他のパッケージにあるのかもしれない。
# pyenv init -
export PATH="/var/root/.pyenv/shims:${PATH}" export PYENV_SHELL=sh command pyenv rehash 2>/dev/null pyenv() { local command command="${1:-}" if [ "$#" -gt 0 ]; then shift fi case "$command" in activate|deactivate|rehash|shell) eval "$(pyenv "sh-$command" "$@")";; *) command pyenv "$command" "$@";; esac }