ShellScriptでドヤりたいGWアドベントカレンダー4日目いくで。
皆さん.zshrcや.bashrcは充実していますか?
Vimの世界では「.vimrcの量=vim力」と言われているらしいですが、shellも似たようなものでしょうか。
それはさておきshellには様々な関数がありますよね。zshやbashで標準で組み込まれている関数やzshrcなどに書き込んだ自作の関数。
「あの関数どうやって書いてたかなあ」とか「あれってどうやって実装されてるんだ?」といったときに便利なやつを作ってみた。
流し読みしながら気になる関数の中身を出力する
みんな大好きfzfを利用しています。
左側に定義済みの関数名、右側にその中身をプレビューで出力しています。
ENTER押したらその内容を出力するものです。
ちなみにこれ地味に苦労した。。。どこに詰まったかは後述する。
とりあえず最終形だけ載せておきますね。
.zshrcに記載してください。bashの人はsource .zshrc
のところを.bashrcに書き換えてね
function func() { local func=$( typeset -f \ | grep ".*() {$" \ | grep "^[a-z_]" \ | tr -d "() {" \ | fzf --height 100% --preview "source ~/.zshrc; typeset -f {}" ) if [ -z "$func" ]; then return fi typeset -f $func }
定義済み関数を全て出力する
これはtypeset -f
で一発ですね。
$ typeset -f _SUSEconfig () { # undefined builtin autoload -XUz } __phpbrew_load_user_config () { if [[ -f $PHPBREW_HOME/init ]] then . $PHPBREW_HOME/init __phpbrew_set_path fi } ...
zsh
だと同様の出力をするfunctions
という関数があります。
$ functions _SUSEconfig () { # undefined builtin autoload -XUz } __phpbrew_load_user_config () { if [[ -f $PHPBREW_HOME/init ]] then . $PHPBREW_HOME/init __phpbrew_set_path fi } ....
ただfunctions
関数はman zshbuiltins
で見てみると、
functions [ {+|-}UkmtTuWz ] [ -x num ] [ name ... ] functions -M [-s] mathfn [ min [ max [ shellfn ] ] ] functions -M [ -m pattern ... ] functions +M [ -m ] mathfn ... Equivalent to typeset -f, with the exception of the -x, -M and -W options. For functions -u and functions -U, see autoload, which provides additional options. ....
Equivalent to typeset -f
となっているのでtypeset -f
と同じであることがわかります。
関数名だけ出力する
シンプルにgrep
の正規表現で抜き取ります。
$ typeset -f \ | grep ".*() {$" \ | grep "^[a-z_]" \ | tr -d "() {" \ _SUSEconfig __phpbrew_load_user_config __phpbrew_reinit __phpbrew_remove_purge __phpbrew_set_lookup_prefix __phpbrew_set_path ...
たぶんもっと良い正規表現があると思いますが、とりあえずこれで十分でしょう。
fzfで関数の中身をpreviewする
ここがめちゃくちゃ詰まったんですよねえ・・・ 関数の中身を出力するには
$ typeset -f 関数名
で出力できるので、普通にfzf --preview 'typeset -f {}'
でイケるやろ!と思ってました。下のような感じで。
typeset -f \ | grep ".*() {$" \ | grep "^[a-z_]" \ | tr -d "() {" \ | fzf --preview 'typeset -f {}'
するとね、何も表示されないんですよ。。。
なんでや!?と思いxargsで渡す方法にしてみた。
fzf --preview 'echo {} | xargs typeset -f'
すると以下のエラー文がプレビューに表示された。
xargs: typeset : No such file or directory
なんですかこれ・・・
これ調べてみると割と既知のエラーらしく、どうやらxargsにshellの関数を渡すときは一工夫しないといけないらしい。 詳しくは以下のサイトが参考になる。
stackoverflow.com unix.stackexchange.com
ということでどうしようかと色々調べていると、fzfのissuesに同じ質問があった。
Bash functions are not visible to child processes if you don't explicitly export them.
どうやら子プロセスではshell関数って表示されないみたい。定義されていないとみなされるのかな?
ということでissuesにも載っているfzfのpreview内でsource ~/.zshrc
することで落ち着いた。
$ typeset -f \ | grep ".*() {$" \ | grep "^[a-z_]" \ | tr -d "() {" \ | fzf --height 100% --preview "source ~/.zshrc; typeset -f {}"
やったぜ。(ただ毎回source
するからちょっと遅い)
終わり
最初思いついたときはすぐ出来るだろと思っていたが、思わぬところで詰まった。
ただこういうところからコマンドの知識が増えていくのがShellScriptの醍醐味ではある。