自作コマンドに専用の補完を実装したいので調べてみた。
成果物
情報源
技術キーワード
bash-completion
complete
COMPREPLY
COMP_CWORD
COMP_WORDS
compgen
-W
: 補完候補を指定する-c
: command, file, directory を補完候補にする-f
: file を補完候補にする-d
: directory を補完候補にする
/usr/share/bash-completion/completions
- [_get_comp_words_by_ref](https://github.com/scop/bash-completion/blob/c16826ee35ecb405fe87007404d0fb846ad61a15/bash_completion#L343-L369
- https://github.com/scop/bash-completion#faq)関数
~/.bash_completion
~/.local/share/bash-completion
$BASH_COMPLETION_USER_DIR
$XDG_DATA_HOME/bash-completion
0
インストール確認
dpkg -l | grep bash-completion
ii bash-completion 1:2.8-6 all programmable
インストールする
sudo apt install bash-completion
1
ターミナルを起動して以下コマンドをコピペする。mycmd
というコマンドの候補としてone two
を表示するよう実装した。
TARGET_CMD=mycmd CAND="one two" SetCompleteCandidates() { COMPREPLY=($CAND); } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
上記のあとで以下コマンドを叩く。<TAB>
はキーボードのTAB
キーを押下すること。
mycmd <TAB>
出力結果。TABキーを押下するとone two
という設定したとおりの候補が出力される。
$ mycmd one two
2
TARGET_CMD=mycmd CAND="one two" SetCompleteCandidates() { echo; echo ${COMP_CWORD}; echo ${COMP_WORDS[@]}; } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
mycmd <TAB>
出力結果。Enter
キー押下することで完了する。
$ mycmd
1
mycmd
スペースで区切るごとに引数の数が増えていく。
mycmd a <TAB>
$ mycmd
2
mycmd a
3
_get_comp_words_by_ref関数を使ってみる。bash-completion
がインストールされているなら使えるはず。
TARGET_CMD=mycmd CAND="one two" SetCompleteCandidates() { local cur prev cword; _get_comp_words_by_ref -n : cur prev cword; COMPREPLY=($CAND); echo; echo ${COMP_CWORD}; echo ${COMP_WORDS[@]}; echo ${COMP_WORDS[COMP_CWORD]}; echo cur: $cur; echo prev: $prev; echo cword: $cword; } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
mycmd a b c<TAB>
出力結果。
$ mycmd a b c 3 mycmd a b c c cur: c prev: b cword: 3
末尾にスペースを入れてからTAB
キーを押下してみる。
mycmd a b c <TAB>
出力結果。
$ mycmd a b c 4 mycmd a b c cur: prev: c cword: 4
cur
とpre
の値が変わった。
出力すべき候補は最後の入力値cur
である。スペースの直後でTAB
キーを押下したらcur
がない。もしcur
がなければpre
を使えばよい。それを_get_comp_words_by_ref
関数を使わずにやると以下。
cur=${COMP_WORDS[COMP_CWORD]}; pre=${COMP_WORDS[${COMP_CWORD}-1]}; target=${$cur:-$pre}; echo $COMP_CUR echo $COMP_PRE echo $COMP_TARGET
TARGET_CMD=mycmd CAND="one two" SetCompleteCandidates() { cur=${COMP_WORDS[COMP_CWORD]}; pre=${COMP_WORDS[${COMP_CWORD}-1]}; # target=${${cur}:-"${pre}"}; # {:-ここを変数にするとエラー。} 「${${cur}:-${pre}}: 誤った代入です」 target=$cur; [ -z "$target" ] && target=$pre; echo ; echo cur: $cur; echo prev: $prev; echo target: $target; } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
4
先頭一致により絞り込む。
TARGET_CMD=mycmd CAND="one once two three" SetCompleteCandidates() { local cur prev opts; _get_comp_words_by_ref -n : cur prev; COMPREPLY=( $(compgen -W "$CAND" -- "$cur") ) } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
mycmd o<TAB>
$ mycmd on once one
5
ソフトウェアにありそうな汎用サブコマンドを使ってみる。
TARGET_CMD=mycmd CAND="help version history license author copylight update homepage sourcecode list" SetCompleteCandidates() { local cur prev opts; _get_comp_words_by_ref -n : cur prev; COMPREPLY=( $(compgen -W "$CAND" -- "$cur") ) } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
mycmd h<TAB>
$ mycmd h help history homepage
ソフトウェアにありそうなサブコマンド一覧。
サブコマンド | 概要 |
---|---|
help |
ヘルプを表示する。ascii, html |
version |
バージョンを表示する。 |
history |
履歴を表示する。 |
created |
最初に公開された日付を表示する。since (〜からずっと) |
updated |
最後に公開された日付を表示する。until (〜までずっと) |
license |
ライセンスを表示する。SPDX形式,GitHub形式。FullName。 |
author |
著者名。 |
copylight |
著作権表示。© 2021 ytyaru , (c) ,(C) , Copylight |
update |
アップデートする。upgrade , downgrade |
homepage |
ホームページURLを表示する。 |
sourcecode |
ソースコードURLを表示する。 |
list |
メタデータの一覧を表示する |
6
ログイン時に自作の補完データを読み込むようにする。
~/.bash_completion
ファイルを作るmycmd
コマンド用の補完作成スクリプトを作る- 使ってみる
6-1. ~/.bash_completion
ファイルを作る。
vim ~/.bash_completion
~/.bash_completion
for file in `find $HOME/.local/share/bash-completion -type f`; do . "$file" done
6-2. mycmd
コマンド用の補完作成スクリプトを作る
mkdir -p ~/.local/share/bash-completion cd ~/.local/share/bash-completion vim mycmd
~/.local/share/bash-completion/mycmd
TARGET_CMD=mycmd CAND="help version history license author copylight update homepage sourcecode list" SetCompleteCandidates() { local cur prev opts; _get_comp_words_by_ref -n : cur prev; COMPREPLY=( $(compgen -W "$CAND" -- "$cur") ) } Set() { complete -F SetCompleteCandidates $TARGET_CMD; } Set
6-3. 使ってみる
新たにターミナルを起動する。そして以下のように入力する。
mycmd h<TAB>
h
に先頭一致するコマンドが表示される。
$ mycmd h help history homepage
課題
- もっと複雑なコマンドへの補完も作りたい
mycmd [sub1] [sub2] [sub3] [-optional] [--optional] [positional]
実装がめちゃくちゃ複雑になってしまいそう。
所感
大変だった。もっと簡単に実装したい。コマンドの仕様をファイルに定義したら、それに基づいてタブ補完やヘルプを自動作成してほしい。そんなツールがあればいいのになぁ。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux