bash-completionと自作シェルその後
Thu Mar 6 10:40:57 JST 2025 (modified: Thu Mar 6 12:27:36 JST 2025)
views: 115, keywords:自作シェル, sush, 寿司シェル, bash-completion
前回、bash-completionが動いたと書いたんですが、当然「動いた」と「使える」は違うわけでいろいろ直していました。で、やっと自分で使ってまあこんなもんかなというところまできたのでまたメモを書いておきます。
gitの補完の謎
まずあのあと、git
の後のサブコマンド(add
やbranch
とかのアレ)の補完ができないかいろいろ調べていました。どのコマンドがどんなふうに補完されるかは、Bashではcomplete
というコマンドで調査できます。次の例はLinuxのBashの例です。
### Bashの例 ###
complete | head -n 3
$ complete -F _longopt mv
complete -F _root_command gksudo
complete -F _command nice
出力の読み方は、たとえば1行目については「mv
は_longopt
という関数をつかって補完候補を見つける」というふうに読みます。_longopt
というのはコマンドのロングオプション(とファイル)を探す関数で、Bashがbash-completionを読み込んだときに一緒に読み込まれます。
ということは、git
の補完方法についてはcomplete | grep git
とすれば探せるはずですが、立ち上げたばかりのBashの場合、
### Bashの例 ###
complete | grep git
$ $ #何も出てこない
というように空振ります。
じゃあどうやって補完しているんだという話になりますが、1回git
で補完をしてcomplete
の出力を調べると、
### Bashの例 ###
git <tab> #←なにか補完を試みる(なんでもよい)
$ complete | grep git
$ complete -o bashdefault -o default -o nospace -F __git_wrap__gitk_main gitk
complete -o bashdefault -o default -o nospace -F __git_wrap__git_main /usr/bin/git
complete -o bashdefault -o default -o nospace -F __git_wrap__git_main git
というように、しれっとgit
の項目ができているのがわかります。
補完機能の自動ロード
ということは自動で補完機能がロードされているということになります。で、どうやってということになるんですが、「デフォルトの補完機能」というものがあり、これを調べると手がかりになります。
### Bashの例 ###
complete | grep 'complete .* -D'
$ complete -F _comp_complete_load -D
これは「補完対象に対応する補完機能が見当たらない場合、_comp_complete_load
で補完しろ」ということを意味します。で、_comp_complete_load
がなにかやってるなということになります。実際、コマンドに対応する機能をロードしているのはこの関数から呼ばれている_comp_load
です。_comp_load
のコードはここで読めます。めっちゃ長いですが。わたしは読みましたよ。読んだというか自分の作っているシェルに文法全部理解させましたよ。ええ。死ぬ。
ということで
自作シェルでもcomplete -D
の関数が呼ばれるようにして、このたびめでたくgit
の補完ができるようになりました。やってることがまともではないので誰も褒めてくれませんが褒めて褒めて。
これでgitのサブコマンドの補完と、そのあとの補完がエラーなく動くようになりました #自作シェル pic.twitter.com/pNkgzxTt6U
— 上田隆一 (@ryuichiueda) February 26, 2025
ブランチの補完もうごきましたー🎉 #自作シェル pic.twitter.com/7vzBG6zWT8
— 上田隆一 (@ryuichiueda) February 26, 2025
_comp_complete_load
が呼ばれている様子です。
### 自作シェルの例 ###
sush
$ Rusty Bash (a.k.a. Sushi shell), version 1.0.4 - release
complete | grep git
🍣 git a #なんか補完してみる
🍣 add am archive apply
C
^complete | grep git
🍣 complete -F __git_wrap__gitk_main gitk
complete -F __git_wrap__git_main git #セットされている
#今後の課題: ほんとはもうひとつ読み込まれるはずだけど読み込まれていない#
さらにその後
git
に気をとられてたらcd
とかvim
とかの補完がうまく動かなくなったことに気づいて直してました。いまは直したものが最新のリリースになっているので使ってみてissueに文句でも書いていただければ幸いです。
現場からは以上です。