【zsh】global aliasを展開する関数を作る(作った)


長くなった。 変数展開でうまいこと書けないかなぁ→書けた って記事

拝啓

zsh の global alias について軽く説明すると
通常のalias と違って、コマンド以外の場所でも 展開されるalias です
大体パイプと組み合わせて定義されてることが多いです
alias名には特にきまりはないですが、global alias名は大文字で定義するのが一般的ぽいですね

alias -g G='| grep'
alias -g H='| head'
alias -g L='| less'

このように定義しておくと、コマンド実行時に展開されます

$ cat log H -n2 
# => cat log | head -n2 


そのまま使ってても便利なのですが、 コマンドライン操作中に自動で展開したら個人的にスマートですね。履歴にも展開後のコマンドとして残るので

alias展開関数はいつくかの記事で紹介されてますが

  • 展開バインドキーはSpace
  • global alias だけ展開する
  • 定義済みのaliasと比較して展開する

みたいなのが個人的にほしかったので勉強がてら作りました

作った

そんな関数がこちら

expand-alias-widget
#!/usr/bin/env zsh

function main() {
  emulate -L zsh
  setopt aliases
  local -a global_aliases=(${(@f)"$(alias -g)"})
  local -a characters=(${global_aliases%%\=*})
  if (($characters[(I)${(q)LBUFFER##* }])); then
     zle _expand_alias
     zle expand-word
  fi  
  zle self-insert
}

main

趣味記法なので関数で囲わなくてもいいし、shebangもいらないかもです
こちらを fpathが通ったディレクトリに置いて、bindkeyを割り当てると使えます

.zshrc
autoload -Uz expand-alias-widget
zle -N expand-alias-widget
bindkey ' ' expand-alias-widget

解説

shellscript内でもaliasを使えるようにする

デフォだと使えないんです。なので オプションを指定してあげる
関数内でオプションを触る時は local_optionsemulate -L zsh しましょう


setopt aliases

定義済みのglobal alias一覧を配列化

alias -g で定義済みのglobal aliasが出力できます
出力は改行ありなので f フラグで 改行文字区切りで単語展開
@フラグで個々の要素を別々の要素として展開(いらないかも)

local -a global_aliases=(${(@f)"$(alias -g)"})

配列からalias 名のみ取り出す

L='| less' から L を抽出する。 ${*name*%%*pattern*} 記法
冒頭の例だとこの時点でcharactersの内容は (G H L)こんな感じになる

local -a characters=(${global_aliases%%\=*})

カーソル左の文字列が、alias名配列に含まれるか調べる

if (( ${array[(I)pattern]} )) で 配列に特定の要素が含まれるかチェックできる

if (($characters[(I)${(q)LBUFFER##* }]))

${(q)LBUFFER##* }はカーソル左の空白区切り末尾文字を取得
(q) が大事
${LBUFFER##* } には *?が展開されることもあるので
パターンマッチで条件式が意図せず真になってしまいます。
そこでqフラグで メタ文字をバックスラッシュでクォートして防ぎます

zleの組み込みウィジェット

alias を展開するだけだったら 既存でwidgetが用意されている
それが _expand_alias
expand-word はシェル展開 (コマンド展開とかグロブ展開とか)

まとめ

え?「変数展開とかフラグが多すぎてわかんないよ〜」って?
まとめといたよ
https://gist.github.com/sho-t/d9cdf8271b3de7c4238739e523490542