以前、MacBook と Mac mini Server の間で git clone しようとして失敗したことがあった(→「MacBook にも Git を (あるいはリモートリポジトリの準備)」)。原因は、リモート側のシェルの PATH 設定だった。git でリモートリポジトリから SSH 経由で clone しようとする場合、リモート側で(SSH 経由で) git コマンドが実行される。リモート側のシェルのパスに git コマンドをふくむディレクトリが入っていなければ、git コマンドが見つからずエラーになる。
シェルは起動時に設定ファイルを読み込む(実行する)ことで実行環境を整える。このとき、たいていのシェルでは実行モードによって読み込む設定ファイルが変わる。実行モードというのはログインシェル、対話シェル、そしてスクリプト実行用シェルの 3 つのことで、git clone で起動されるシェルはスクリプト実行用シェルになる。このため、たとえ ssh コマンドでリモートログインした状態で対話的に git コマンドを使えたとしても、git clone しようとすると (git コマンドが見つからず)エラーになることがある。
今日も、MacBook と iMac の間で git clone しようとして同じエラーが出て失敗した。以前解決したはずの問題にまた遭遇したことになる。というのも、あれからしばらくしてシェルを zsh に変えたからだ(→「twitter より (2010-08-03)」)。最初の設定のときに、ssh コマンドでリモートログインしてローカルなシェルの場合と同様に使えることを確認して安心していた。今日まで git clone のようなリモートのコマンドを直接実行することはなかったから、この問題に気付かなかった。
では、zsh を使う場合リモート(のスクリプト実行用)のシェルが参照するパスを設定するにはどうすれば良いのか? より具体的に言えば、git のリモートリポジトリを置いたコンピュータのシェルが zsh のとき、git clone 等が失敗しないようにするにはどうすれば良いのか?
答えは ~/.zshenv
結論を先に書くと、スクリプト用シェルのために PATH を設定するには ~/.zshenv に書けば良い。書式は通常のシェルスクリプトと同じ。つまり、git clone が失敗する問題を解決するためには、リモート側(clone されるリポジトリがある方)に ~/.zshenv を作り、以下のように PATH の設定をしてやれば良い(git コマンドを MacPorts でインストールしている場合)。
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
これまで、zsh のためのパスは ~/.zshrc で設定してきた。この方法どこが悪かったのだろう?
それは実行モードごとの設定ファイルの読み込みが以下のようになっているからだ(→「【コラム】漢のzsh (1) 最強のシェル、それは「zsh」 | マイコミジャーナル」参照)。一言で言えば、~/.zshrc はスクリプト実行用シェルのときは読み込まれない。
実行モード | 読み込み順 |
---|---|
ログインシェル |
|
対話シェル |
|
スクリプト用シェル |
|
これを見れば、スクリプト用シェルが参照する PATH は ~/.zshenv で設定しなければならないことがわかる。
~/.zshenv を読むより前に起きていること
さて、これで MacBook と iMac の間で git clone に失敗する問題は解決できたが、ついでに zsh における PATH の設定について調べてみた。
zsh の man ページによれば、シェルが起動時に最初に読み込むのは /etc/zshenv だとのこと。その後、上述の表のような順序で設定を読み込む。では、/etc/zshenv はどうなっているのか?
Mac OS X 10.6.5 の /etc/zshenv の内容を以下に示す。
# system-wide environment settings for zsh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi
ここに書いてあるのは /usr/libexec/path_helper があれば、それを実行しろというもの(バッククォート記法なので、正確には path_helper の実行結果をスクリプトとして実行しろ、となる)。
この path_helper というコマンドは PATH を設定するためのシェルスクリプトを生成するためのもので -s オプションで B-shell 用のスクリプトが作られる。man ページには直接実行するものじゃないゾ、と書いてあるが、そこをあえて実行してみた結果を以下に示す。実行の前に PATH と MANPATH の設定を消しているのは、このコマンドが現在の設定に追加するようになっているから。空にしておくことで、zsh が /etc/zshenv でこのコマンドを実行したときの結果がわかる。
[imac] mnbi% PATH='' [imac] mnbi% MANPATH='' [imac] mnbi% /usr/libexec/path_helper -s PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin"; export PATH; MANPATH="/usr/share/man:/usr/local/share/man:/usr/X11/share/man"; export MANPATH;
では、path_helper は PATH (と MANPATH) に入れるべきディレクトリをどうやって知るのだろう? man ページによれば、それは /etc/paths と /etc/manpaths だとのこと。そして、ここに書かれたものに、さらに /etc/paths.d と /etc/manpaths.d に置かれたファイルの内容を付け加えて、PATH と MANPATH とする。
以下に、Mac OS X 10.6.5 の /etc/paths の内容を示す。
/usr/bin /bin /usr/sbin /sbin /usr/local/bin
同じく、Mac OS X 10.6.5 では /etc/paths.d には X11 というファイルがあり、その内容は以下のようになっている。
/usr/X11/bin
以上のことから、~/.zshenv を使ってユーザごとにパスを設定する以外にも、追加したいパスを書いたファイルを /etc/paths.d に置けば、システム全体で(正確には zsh を使っているすべてのユーザで)同様の効果が得られることがわかる。
関連リンク
- 【コラム】漢のzsh (1) 最強のシェル、それは「zsh」 (マイコミジャーナル)
関連記事
- MacBook にも Git を (あるいはリモートリポジトリの準備)
- twitter より (2010-08-03) (iMac が届いた記念にシェルを tcsh から zsh に変えたというつぶやき)