こんにちは、エンジニアのitogaです。

非常事態宣言の中みなさんの開発の進捗も滞っていることと思います。利用するCLIツールが増えるにつれて、こんなタブ補完があったら良いなぁと思いを馳せる頃でしょうか。今回は自作タブ補完の入門として、sshコマンドのタブ補完で複数のconfigファイルのホストを表示することを目標に進めていきます。

せっかちな人向けの、複数configファイルに対応したsshコマンドのタブ補完

以下のgrep対象のディレクトリパスを環境に合わせて変えたものをログインシェルに追記してあげてください。

_ssh()
{
  local opts
  opts=$(grep '^Host ' ~/.ssh/config ~/.ssh/conf.d/* 2>/dev/null | grep -v '[?*]' | cut -d ' ' -f 2-)
  COMPREPLY=( $(compgen -W "${opts}" -- "${COMP_WORDS[${COMP_CWORD}]}") )
}
complete -F _ssh ssh

まずは、complete関数でタブ補完を触ってみる

早速sshコマンドのタブ補完をいじってみましょう。まず、変数COMPREPLYに配列を定義する関数を定義します。

$ _dummy_ssh() { COMPREPLY=(apple orange apricot); }

次に、この関数をcomplete関数の-Fオプションに渡し、タブ補完するコマンド名sshを末尾に記載し実行します。

$ complete -F _dummy_ssh ssh

そして、sshコマンドのタブ補完を試してみると無事変更できたことが確認できます。

$ ssh
apple    apricot  orange

補完対象を絞り込み

現状だと、途中まで入力した状態でタブを押しても絞り込みや入力まではされません。それを実現するためにはCOMP_CWORD、COMP_WORDSという変数とcompgenという関数を使います。

COMP_CWORDとCOMP_WORDS

$ _dummy_ssh()
{
  echo
  echo COMP_CWORD: ${COMP_CWORD}
  echo COMP_WORDS: ${COMP_WORDS[@]}
  echo PREV_WORDS: ${COMP_WORDS[${COMP_CWORD}-1]}
}

COMP_CWORDは入力中の語句のインデックス番号、COMP_WORDSは入力中のコマンド全体の語句の配列になっています。

$ ssh first sec
COMP_CWORD: 2
COMP_WORDS: ssh first sec
PREV_WORDS: first

compgen

compgen関数は、与えられた文字列のリストに対して前方一致する文字列のみを返す関数です。

$ compgen -W "apple orange apricot" --
apple
orange
apricot
$ compgen -W "apple orange apricot" -- a
apple
apricot
$ compgen -W "apple orange apricot" -- app
apple

タブ補完で補完対象を絞り込み

compgenとCOMP_CWORD、COMP_WORDSを組み合わせて以下のような関数をcomplete関数に渡します。

$ _dummy_ssh()
{
  local opts
  opts="apple orange apricot"
  COMPREPLY=( $(compgen -W "${opts}" -- "${COMP_WORDS[${COMP_CWORD}]}") )
}

すると、親の顔より見慣れたタブ補完を実装できます。

$ ssh a
# タブを押す
$ ssh ap
# タブを2回押す
$ ssh ap
apple    apricot
$ ssh app
# タブを押す
$ ssh apple

sshコマンドのタブ補完で複数のconfigファイルのホストを表示する

あとは、grep,cutを駆使して補完対象リストの文字列を生成してあげるだけで実装完了!!complete関数の実行も忘れずに。

$ _ssh()
{
  local opts
  opts=$(grep '^Host ' ~/.ssh/config ~/.ssh/conf.d/* 2>/dev/null | grep -v '[?*]' | cut -d ' ' -f 2-)
  COMPREPLY=( $(compgen -W "${opts}" -- "${COMP_WORDS[${COMP_CWORD}]}") )
}
$ complete -F _ssh ssh

おわりに

いかがでしたでしょうか。この関数の定義とcomplete関数の実行を、bash_profileなどのログインシェルに記載すればいつでもコマンド補完ができます。このCLIツール、機能としては便利だけどオプションや引数のタイプが面倒なんだよなぁ、という惜しい局面で役立ていただければ幸いです。

Pocket