git-sh-setupが見付からない
$ git pull -h
/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git-pull: line 12: git-sh-setup: No such file or directory
元旦早々こんな問題が発生。
実は前にも見たけどなんとなくスルーしてしまった。今回、ちょっと気になったので調べてみると、どうも端末エミュレータによって発生したりしなかったりするらしい。実際Teriminal.appでは問題なくて、iTerm2だと上のようになる。いったい何が違うのん?
(あ、ちなみに新しめのiTerm2なら修正済みという話を見掛けました。よって、ここからの追求は多分あんまり意味がないです。また、以下の現象がよそで起きている現象と同じかどうか分かりません。どうして古いiTerm2を使い続けているのかというと、新しいiTerm2だとMacUIM-tutcode/tcodeが動かないという別の問題がありまして……。AquaSKKもだっけ? まあ、新しいiTerm2にpatch適用するのをめんどくさがっているだけなのですが。参考: iTerm2 + AquaSKK)
さて。エラーが起きているgit-pull
の11行目はこんな記述だった。
. git-sh-setup
これがない、と。でもls
するとある。そもそもTerminal.appなら動いている。少し検索してみるとpopenがうんぬん、環境変数がうんぬんという意見が出てきた。なるほど「.
」はPATH
に従う。
ひとまず確認をと思い、10行目にecho $PATH
を埋めてgit pull -h
を実行してみた。まずは問題のないTerminal.appから。
$ git pull -h
/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/heroku/bin:/Users/akira/bin:/usr/local/bin:/usr/local/sbin:/Users/akira/.rbenv/shims:/Users/akira/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
usage: git pull [-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...
[...]
そして困ったことになっているiTerm2。
$ git pull -h
/usr/bin:/bin:/usr/sbin:/sbin
/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git-pull: line 12: git-sh-setup: No such file or directory
おー、そうですか。いや、そうですよね。ちなみにこのときのPATH
はがどうなっているのかなと見てみると、いずれの環境でも以下の通りだった。
/usr/local/heroku/bin:/Users/akira/bin:/usr/local/bin:/usr/local/sbin:/Users/akira/.rbenv/shims:/Users/akira/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
と、ここで気付く。環境変数を表示させるのにenv
を使ったのだがiTerm2での結果がおかしい。
$ env | grep PATH
PATH=/usr/local/heroku/bin:/Users/akira/bin:/usr/local/bin:/usr/local/sbin:/Users/akira/.rbenv/shims:/Users/akira/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PATH=/usr/bin:/bin:/usr/sbin:/sbin
何これ? ps
でも見てみる。(3222はiTerm2上で動いているシェル。)
$ ps -wwwE -o args -p 3222
ARGS
-zsh PATH=/usr/bin:/bin:/usr/sbin:/sbin USER=akira PATH=/usr/bin:/bin:/usr/sbin:/sbin LOGNAME=akira SSH_AUTH_SOCK=/private/tmp/...(略)...
えーと、PATHの内容が違うのはプロセス起動時のものということで、それはともかくとして二つのPATHがあるのを確認できる。ちなみにTerminal.appのほうはやはり問題ない。(2875はTerminal.app上で動いているシェル。)
$ ps -wwwE -o args -p 2875
ARGS
-zsh TMPDIR=/var/folders/...(略)... LOGNAME=akira USER=akira PATH=/usr/bin:/bin
ただ、よく分からないのは、git
から起動されるgit-pull
プロセスの環境変数にgit用のパスが加わっていないこと。このパスを加えているのはgit
なはずで、fork()
/exec()
するのもgit
のはず。つまり、git-pull
を見付けられないって話ならまだ分かるのだけど、一段とんでgit-sh-setup
なのがどういうことなのか。
また、iTerm2上で別プロセスを起動して、そこからgit pull -h
するとちゃんと思く。どういうこと?
$ ruby -e 'system "git pull -h"'
usage: git pull [-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...
[...]
ではgit-pull
の環境変数はどうなっているか。10行目にenv | grep PATH
をいれてみる。
$ git pull -h
PATH=/usr/bin:/bin:/usr/sbin:/sbin
/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git-pull: line 12: git-sh-setup: No such file or directory
おや一つしかない。念のためps
でも確認。10行目をps -wwwE -o args -p $$
に変更する。
$ git pull -h
ARGS
/bin/sh /Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git-pull -h PATH=/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/heroku/bin:/Users/akira/bin:/usr/local/bin:/usr/local/sbin:/Users/akira/.rbenv/shims:/Users/akira/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin USER=akira PATH=/usr/bin:/bin:/usr/sbin:/sbin LOGNAME=akira SSH_AUTH_SOCK=/private/tmp/...(略)...
/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git-pull: line 12: git-sh-setup: No such file or directory
ここから、git-pull
の起動時には二つのPATH
があったことをうかがえる。そして一つはgit用パスを含み、もう一つは含まない。含まないほうはデフォルトのパスのようでもある。git-pull
プロセス中でPATH
が一つになっているのは/bin/sh
による初期化処理か何かによるものだと推測できる。
ここでちょっと実験。二つあるPATHのどっちが効いてるのん?
まず、おさらい。iTerm2上での二つのPATH
を確認する。
$ env | grep PATH
PATH=/usr/local/heroku/bin:/Users/akira/bin:/usr/local/bin:/usr/local/sbin:/Users/akira/.rbenv/shims:/Users/akira/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PATH=/usr/bin:/bin:/usr/sbin:/sbin
二つめのPATH
の内容は一つめにも含まれるので、一つめのPATH
をちょっといじる。
$ PATH=/usr/local/bin
二つのPATH
がどうなったか確認する。
$ env |grep PATH
zsh: command not found: env
zsh: command not found: grep
おっと。
まあ、このミスですでに分かったような部分もあるがもう改めて。
$ /usr/bin/env |/usr/bin/grep PATH
PATH=/usr/local/bin
PATH=/usr/bin:/bin:/usr/sbin:/sbin
二つめのPATH
にしかないenv
やgrep
は実行できないのをうっかり確認してしまったが、一つめのPATH
、つまり/usr/local/bin
にしかないコマンドは実行できることも確認しておく。
$ brew -h
Example usage:
brew [info | home | options ] [FORMULA...]
[...]
つまり一つめのPATHが効いている。一つめか二つめかというのは表示上の順番でもあるから、どっちがどうというのはおいておいて、ともかくどちらか一方だけが有効だと分かる。となるとこういうことだろうか。
- iTerm2上のシェル - 二つの
PATH
を持つ git
プロセス - 二つのPATH
を持ち、そのうち一つにgit用パスを追加するgit-pull
プロセス - 二つのPATH
を与えられて起動するが、本体のシェルスクリプト実行時には一つだけが残されもう一つは消えている
git-pull
プロセス上ではデフォルトの簡素なPATH
が残っているようで、つまり、このためにgit-sh-setup
を見付けられないということだ。
一応、回避方法はgit
に二つのPATH
を渡さないことだろうか。幸いというかなんというか、iTerm2上で動くシェル自身は、デフォルトの簡素なパスではなくて自分で設定したそれなりのパスが有効になっている。(そのせいで今回の問題に気付かなかったという面がある。) よって、どこか適当なところにgit
のwrapperを置いておけばよい。
$ cat ~/bin/git
#!/bin/sh
exec /usr/bin/git "$@"
まあ、こういうの作ると忘れたころに弊害をくらうものなのだけど。というわけで、冒頭に書いた通り、iTerm2を更新するなどするのがまっとうなんだろう。