shellだけでdirenv的な事をする

direnv、便利ですが人によっては入れたくないという思いもありますし、職場でのアンケートを行うとそもそも知らない、という人もいるようです

 

今回のそのそもの目的は.ruby-version、.node-version等から値を環境変数にセットし、それを更にRails、Docker、Docker Compose等に渡したいという要望です

これを行うと、.ruby-version、.node-versionだけの変更で一括でバージョン変更が可能となりアップデートが簡単に行えます

 

Zsh

個人的には未だにBash愛用者ですが、仕事用環境はmacOSですが現在はデフォルトshellがZshとなっていますので対応が必要です

zshではコマンドにHookを行うことが可能です この機能を使って対応します

.zsh_config を対象ディレクトリに置きます 中身はこの様にします

function setter() {
  export HOGE=1
}

function unsetter() {
  unset HOGE
}

case $1 in
set)
  setter
  ;;
unset)
  unsetter
  ;;
esac

~/.zshrc 側でコマンドをHookします

# cdで移動前に現在のディレクトリの設定を削除
function unset_dir_config() {
  if [ `echo $1|awk '{print $1}'` = 'cd' ]; then
    if [ -r $PWD/.zsh_config ]; then
      source $PWD/.zsh_config unset
    fi
  fi
}

# cdで移動跡に現在のディレクトリの設定を行う
function set_dir_config() {
  if [ -r $PWD/.zsh_config ]; then
    source $PWD/.zsh_config set
  fi
}
add-zsh-hook chpwd set_dir_config
add-zsh-hook preexec unset_dir_config

非常に簡単に設定が行えます

Bash

私は普段 はBashを使用しています。そろそろZshにしても良いかもとは思ってますが、あまり慣れるとBash環境を触る時に辛そうだなぁと思いまだ躊躇していますね

さて、Bashではデフォルトの機能ではコマンドをHookする機能が無い?ようでbash-preexecを利用します github.com

.bash_config.zsh_config とファイル名が違うだけで同じで内容は同じなので省略します

~/.bashrc もほぼ同じです

source ~/bash-preexec.sh
preexec_functions+=(unset_dir_config)
precmd_functions+=(set_dir_config)
unset_dir_config() {
  PRECMD=`echo $1|awk '{print $1}'`
  if [ "$PRECMD" == 'cd' ]; then
    if [ -f $PWD/.bash_config ]; then
      source $PWD/.bash_config unset
    fi
  fi
}

set_dir_config() {
  if [ "$PRECMD" == 'cd' ]; then
    if [ -f $PWD/.bash_config ]; then
      source $PWD/.bash_config set
    fi
  fi
  unset PRECMD
}

応用

特定のコマンドをHook出来るのでそのコマンド専用の環境変数を設定する、何か専用の処理を行う事ができます。 例えば、弊社はCircleCIを利用していますのでCircleCI CLIも利用しています。ローカルでcircleciコマンドを実行する場合にだけ設定を行うという事が可能です

以前から環境変数について悩みどころだったのですが今回必要に迫られたとは言え調べて対応できたので良かったですね 今後色々な場面で利用できそうです

参考

note.hibariya.org