Zshで環境変数とその値を補完する
シェルからコマンドを実行する際、環境変数を都度指定して渡すことがあります。たとえば、Railsにおける RAILS_ENV
や、Google Cloud SDKの gcloud
コマンドを起動する際の CLOUDSDK_ACTIVE_CONFIG_NAME
は代表的な例です。
% RAILS_ENV=production rails assets:precompile
% CLOUDSDK_ACTIVE_CONFIG_NAME=myproject-production gcloud app logs tail
こうした指定はよく行うにも関わらず、タイプ数が多く面倒ですね。シェルの履歴に頼れば省力化はできるものの、変数の値を変えて再実行、のような場合は値を手打ちすることになります。
さて、zshには高機能な補完定義機能がありますが、こうした環境変数名やその値の補完定義を書くにはどうすればいいでしょうか。
変数名を補完する
Zshでは、環境変数名を書けるところでは、定義済みの環境変数名が補完できます。
% PAT ← ここでTABを押す
↓
% PATH=
ところが、 RAILS_ENV
や CLOUDSDK_ACTIVE_CONFIG_NAME
などの変数はふつう定義されていないので、zshはその存在を知る由もなく、補完することができません。
これをzshに教え込むには、 zstyle
コマンドを用いて fake-parameters
に設定します。
zstyle ':completion:*' fake-parameters RAILS_ENV:string CLOUDSDK_ACTIVE_CONFIG_NAME:string
これで、 CLOUDSDK_ACTIVE_CONFIG_NAME
という変数があるということがzshの補完システム(正確には _parameters
関数)に認知されるようになります。
% CLO ← ここでTABを押す
↓
% CLOUDSDK_ACTIVE_CONFIG_NAME=
変数の値を補完する
さて、長ったらしい名前が補完できるようになって楽になりましたが、その値も補完したいですね。こんなところでミスタイプはしたくないところです。 CLOUDSDK_ACTIVE_CONFIG_NAME
の場合は、 gcloud
コマンドで一覧できるconfiguration名の中から選びたいでしょう。
% gcloud config configurations list
NAME IS_ACTIVE ACCOUNT PROJECT COMPUTE_DEFAULT_ZONE COMPUTE_DEFAULT_REGION
default False
myproject-demo False knu@example.com myproject-demo
myproject-production False knu@example.com myproject-production
myproject-test True knu@example.com myproject-test
補完候補の生成
補完定義を書く前に、候補の一覧を生成するコマンドを考えましょう。この出力形式なら、awkを使うのが手軽そうですね。
% gcloud config configurations list | awk 'NR>=2{print $1}'
default
myproject-demo
myproject-production
myproject-test
あるいは、まじめにJSONで処理するのもいいでしょう。
% gcloud --format=json config configurations list | jq -r '.[].name'
default
myproject-demo
myproject-production
myproject-test
補完定義の作成
Zshで変数の補完は -value-,変数名,…
という特別なコンテキスト名で定義します。つまり、次のようなファイルを fpath
のどこかに置けばOKです。
#compdef -value-,CLOUDSDK_ACTIVE_CONFIG_NAME,-default-
compadd -- $(gcloud --format=json config configurations list | jq -r '.[].name')
新たに置いたものを読み込ませるには compinit
を実行します。
% compinit
これで、補完が効くようになりました。
% CLOUDSDK_ACTIVE_CONFIG_NAME= ← ここでTABを押す
default myproject-demo myproject-production myproject-test
全部 .zshrc
の中で管理したいという場合は、自分で関数を定義して compdef
に渡します。
_cloud_active_config_name() {
emulate -L zsh
compadd -- $(gcloud --format=json config configurations list | jq -r '.[].name')
}
compdef _cloud_active_config_name -value-,CLOUDSDK_ACTIVE_CONFIG_NAME,-default-
いろいろな例
ほかにも、アイデア次第で様々な変数の値を補完して便利に使うことができます。
変数名自体も補完できるよう、 fake-parameters
に足すのも忘れないようにしましょう。複数指定する場合はブレース展開を使うと便利です。
zstyle ':completion:*' fake-parameters {RBENV_VERSION,RAILS_ENV,CLOUDSDK_ACTIVE_CONFIG_NAME,ASDF_{ELASTICSEARCH,JAVA,NODEJS,POSTGRES,POSTGIS,PYTHON}_VERSION}:string
RAILS_ENV
冒頭で触れた RAILS_ENV
の例です。
#compdef -value-,RAILS_ENV,-default-
if [[ -d config/environments ]]; then
compadd -- config/environments/*.rb(:t:r)
else
compadd -- production development test
fi
なお、 RAILS_ENV
環境変数を使う代わりに rails
コマンドの -e
オプションを使う手もあります。ただ、 rails の補完定義を頑張って作るよりも手間が少なく、Node.jsの NODE_ENV
やLaravelの APP_ENV
にも応用が利きます。
RBENV_VERSION
Rubyのバージョン管理にrbenvを使っていて、その場限りでバージョンを切り替えたいときに使います。nodenv (NODENV_VERSION
)なども同様です。
#compdef -value-,RBENV_VERSION,-default-
compadd -- $(rbenv versions --bare)
ASDF_NODEJS_VERSION, ASDF_POSTGRES_VERSION, etc.
asdfのように無数のプラグインがあってそれぞれ環境変数がある場合は、パターンマッチで定義して動的に処理します。
#compdef -P -value-,ASDF_*_VERSION,-default-
compadd -- $(asdf list ${(L)compstate[parameter][6,-9]} 2>/dev/null)
調べ方
だいたいはzshのマニュアルページに載っています。 man zshall
で引いてみましょう。 fake-parameters
についての記載はこのセクション、 -value-
についての記載はこのセクションにあります。
あとは、zsh標準で用意されている補完定義にヒントが詰まっています。たとえば、 LANG
, LC_MESSAGES
, LC_TIME
, LC_ALL
などの値に標準でlocale名を補完できるようになっていますが、これは _locales
で定義されています。手元でgrep (ag, rg, …)してみると、どんな補完定義があるのか一望できます。
grep -r '^#compdef' $fpath
Discussion