やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

bashコマンド補完を自作する bash-completion,complete,compgen

 自作コマンドに専用の補完を実装したいので調べてみた。

成果物

情報源

技術キーワード

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

 curpreの値が変わった。

 出力すべき候補は最後の入力値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

 ログイン時に自作の補完データを読み込むようにする。

  1. ~/.bash_completionファイルを作る
  2. mycmdコマンド用の補完作成スクリプトを作る
  3. 使ってみる

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]

 実装がめちゃくちゃ複雑になってしまいそう。

所感

 大変だった。もっと簡単に実装したい。コマンドの仕様をファイルに定義したら、それに基づいてタブ補完やヘルプを自動作成してほしい。そんなツールがあればいいのになぁ。

対象環境

$ uname -a
Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux