Bashのタブ補完って便利ですよね。
私の一押しはgitのコマンドを補完してくれるgit-completion.bashです。
めっちゃ便利なので使ってない人は是非使いましょう。
さて、Bashの補完が作れるということなのですが、実際どう作るのか気になったので調べながら実装しました。
基本的には分かりやすい解説があるので下記のブログを読みましょう。下記のブログの方が分かりやすいし親切です。「同じオプションが2回サジェストされない」実装をしたくなったら戻ってきてください。 - Bashタブ補完自作入門 - Cybozu Inside Out | サイボウズエンジニアのブログ
作ったもの
今回の題材command.py。
command.py --number one --animal cat
とかすると1 cat
と返してくれるコマンド。
英単語をアラビア数字に変換して、animalはあれば続けて出力する。
#!/usr/bin/python3 """ コマンドラインからnumberとanimalを受け取って出力する 実行権限の付与を忘れずに """ import argparse parser = argparse.ArgumentParser() parser.add_argument("--number", help="input one word from zero to nine") parser.add_argument("--animal", help="input one word from cat, dog or deer") args = parser.parse_args() w2n = { 'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, } if args.number: print(w2n[args.number], end=' ') if args.animal: print(args.animal, end='') print()
下記の内容を~/.bash_completion
に追記すると補完が効くようになる。
_except() { # 文字列を集合と見たときの差集合`a - b`を返す local a b intersection difference a=$1 b=$2 intersection=($(for item in ${a} ${b}; do echo "$item"; done | sort | uniq -d)) difference=($(for item in ${a} ${intersection[@]}; do echo "$item"; done | sort | uniq -u)) echo "${difference[@]}" } _command() { local cur prev cword opts numbers animals unused_opts _get_comp_words_by_ref -n : cur prev cword opts="--number --animal" unused_opts="$(_except "$opts" "${COMP_WORDS[*]}")" numbers="zero one two three four five six seven eight nine" animals="cat dog deer" COMPREPLY=( $(compgen -W "${unused_opts[@]}" -- "${cur}") ) if [ "${prev}" = "--number" ]; then COMPREPLY=( $(compgen -W "${numbers}" -- "${cur}") ) elif [ "${prev}" = "--animal" ]; then COMPREPLY=( $(compgen -W "${animals}" -- "${cur}") ) fi } complete -F _command command.py
解説
complete
コマンドで補完時に使う関数を補完したいコマンドに紐つけておくと、関数内でCOMPREPLY
に代入した単語群を使って補完してくれる- ターミナルで入力している内容がスペース区切りで配列として
COMP_WORDS
に渡されている opts
とCOMP_WORDS
の差集合を取ることで、既に使っているオプションサジェストしない実装になっているCOMPREPLY=( $(compgen -W "${unused_opts[@]}" -- "${cur}") )
のところ_except
の解説は別記事(Bashで配列の積集合・差集合を求める)を参照してください
${prev}
(一つ前の文字列)がオプション名だった場合は、それに対応するanimals
やnumbers
から補完させる