NVM_DIRを正しく設定していなかったせいでパスが破壊されていた話
はじめに
特に書くことがなく4ヶ月ぶりのブログです.
最近golangを始めて, export PATH=$GOPATH/bin:$PATH
とかでパスを通そうとしたらなぜかパスが通らないという問題が起きました.いろいろ探っていくとどうやら nvm.sh
をsourceしている場所より後ろに配置すると通るという謎現象が起きていて,さらに言うと $GOPATH/bin
のbinをhogeとか別の名前にすると通るという意味が分からないことになっていたので, nvm.sh
を少し読んでみたら原因がわかったという話です.というかブログのタイトル通りなので,あとはダラダラと書いていきます.
nvm.sh
nvmはNode Version Managerの略でNode.jsのバージョン管理ツールであり,使っている人も多いのではないでしょうか.僕はNode.jsはほぼ書いたことがないのですが,たまに必要になったりすることもあるので一応使っています.
そんなnvmですが,シェルの設定ファイルとかに nvm.sh
を実行するように書いて使用します.この nvm.sh
の中にnvmコマンドの本体も書かれています.
nvm() { if [ $# -lt 1 ]; then nvm --help return fi local DEFAULT_IFS DEFAULT_IFS=" $(nvm_echo t | command tr t \\t) " if [ "${IFS}" != "${DEFAULT_IFS}" ]; then IFS="${DEFAULT_IFS}" nvm "$@" return $? fi ...........................
起きていた現象について
まず, nvm.sh
をsourceしている直前で $GOPATH/bin
のパスを通してから echo $PATH
で正しく反映されていることを確認しました.その後, nvm.sh
の直後にも echo $PATH
を置いて,パスの頭に追加した $GOPATH/bin
がそっくり消えていることも確認しました.これは冒頭でも書いたようにbinを別名にすると通ります.
そこで, nvm.sh
を読むことにしました.この時僕が使っていた nvm.sh
は少し古いものでしたが(ver 0.33.1),最新版(ver 0.33.9)を持ってきてもパスが通らないことに変わりはありませんでした.以下,まずは古い nvm.sh(0.33.1)
について書きます.
nvm.sh
では nvm_process_parameters
という関数を実行していてその中でさらに nvm_auto
を実行しています.この nvm_auto
の中にあった以下の行でパスは更新されていました.
....... VERSION="$(nvm_resolve_local_alias default 2>/dev/null || nvm_echo)" if [ -n "$VERSION" ]; then nvm use --silent "$VERSION" >/dev/null .......
VERSION
には v8.11.1
などが入ってきます.次に,nvmのuseというサブコマンドを見てみます.nvmに渡されたサブコマンドはcase文によって分岐していて,useも同様です.
"use" ) local PROVIDED_VERSION local NVM_USE_SILENT NVM_USE_SILENT=0 local NVM_DELETE_PREFIX NVM_DELETE_PREFIX=0 .......
このuseの中に以下のような行を見つけました.
....... # Strip other version from PATH PATH="$(nvm_strip_path "$PATH" "/bin")" .......
見るからに原因がこれっぽいです....
nvm_strip_path
を見てみると,
nvm_strip_path() { if [ -z "${NVM_DIR-}" ]; then nvm_err '${NVM_DIR} not set!' return 1 fi nvm_echo "${1-}" | command sed \ -e "s#${NVM_DIR}/[^/]*${2-}[^:]*:##g" \ -e "s#:${NVM_DIR}/[^/]*${2-}[^:]*##g" \ -e "s#${NVM_DIR}/[^/]*${2-}[^:]*##g" \ -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*:##g" \ -e "s#:${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*##g" \ -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*##g" }
このように渡された引数を sed を使って置換しています.第1引数には現在のパスを,第2引数には"/bin"という文字列を渡していて,冒頭で説明した追加しているのに消えているパスというのは /home/yyy/.go/bin
です.これは先頭に追加していたので,追加後は /home/yyy/.go/bin:/home/yyy/...
という風になるはずです.
また, NVM_DIR
はどんな値になっているのか調べてみると NVM_DIR=/home/yyy
になっていました.
ということでsedで置換している1行目, ${NVM_DIR}/
は"/home/yyy/"になり, [^/]
は否定の文字クラスであり"/"以外全てを表し,直後の*である0回以上の繰り返しによって".go"が当てはまり,その後の ${2-}
が"/bin,最後の":"も合わせて `/home/yyy/.go/bin:" が当てはまってしまうことになります.
よって,sedのこの1行目でパスが破壊されていたということになります.
NVM_DIRについて
謎の現象が起きていた原因はもちろん, NVM_DIR
を正しく設定していなかったことです.なにを見て追加したかは忘れましたが,とにかく設定ファイルに NVM_DIR
は書いてありませんでした....
それでも NVM_DIR
が設定されていたのは nvm.sh
の以下の箇所が原因でした.
# Auto detect the NVM_DIR when not set if [ -z "${NVM_DIR-}" ]; then # shellcheck disable=SC2128 if [ -n "${BASH_SOURCE-}" ]; then # shellcheck disable=SC2169 NVM_SCRIPT_SOURCE="${BASH_SOURCE[0]}" fi # shellcheck disable=SC1001 NVM_DIR="$(nvm_cd ${NVM_CD_FLAGS} "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)" export NVM_DIR fi unset NVM_SCRIPT_SOURCE 2> /dev/null
NVM_CD_FLAGS
は "-q" になっていて, nvm_cd
というのは以下のようになっています.
nvm_cd() { # shellcheck disable=SC1001,SC2164 \cd "$@" }
また, "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")"
は nvm.sh
があるディレクトリになるので,僕の場合 "$(nvm_cd ${NVM_CD_FLAGS} "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)"
は以下のような文字列になるはずです.
cd -q /home/yyy/.nvm > /dev/null && \pwd
.nvm
の絶対パスを取得して,この結果を NVM_DIR
に格納していると思うのですが, -q
オプションをつけるのとつけないので比較してみると, -q
を付けた場合は格納される値がホームディレクトリの絶対パスになります.そもそも -q
オプションなんて cd にはないと思うのですが,よくわかりません.
NVM_CD_FLAGS
は以下の箇所で定義されています.
# Make zsh glob matching behave same as bash # This fixes the "zsh: no matches found" errors if [ -z "${NVM_CD_FLAGS-}" ]; then export NVM_CD_FLAGS='' fi if nvm_has "unsetopt"; then unsetopt nomatch 2>/dev/null NVM_CD_FLAGS="-q" fi
このコメントを見る限り, NVM_CD_FLAGS
は必要なものだと思います.これを除いて NVM_DIR="$(nvm_cd "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)"
とすると, NVM_DIR
には.nvmの絶対パスが入ってくれるのですが....
最新版のnvm.sh
現在(2018/04/20)の最新版は 0.33.9 です.この nvm.sh
では上記の箇所が以下のように変わっていました.
# Change current version PATH="$(nvm_change_path "$PATH" "/bin" "$NVM_VERSION_DIR")"
nvm_change_path() { # if there’s no initial path, just return the supplementary path if [ -z "${1-}" ]; then nvm_echo "${3-}${2-}" # if the initial path doesn’t contain an nvm path, prepend the supplementary # path elif ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/[^/]*${2-}" \ && ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then nvm_echo "${3-}${2-}:${1-}" # use sed to replace the existing nvm path with the supplementary path. This # preserves the order of the path. else nvm_echo "${1-}" | command sed \ -e "s#${NVM_DIR}/[^/]*${2-}[^:]*#${3-}${2-}#g" \ -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*#${3-}${2-}#g" fi }
nvm_strip_path
ではなく nvm_change_path
を使っています.これも同様に,sedの1行目に当てはまってしまっていたのでパスが消えていたという事でした.
まとめ
結局 NVM_CD_FLAGS
がなぜ必要なのかわからず,バグなのかどうかもわからずじまいでした.issueでも立てたほうがいいんですかね....
NVM_DIR
を正しく設定しましょうという話でした.