では、bash
組み込みコマンドを使用してコマンドパラメータ設定のカスタマイズを完了するのは非常に簡単です。complete
たとえば、仮想コマンドの場合、要約は次のようになります。
foo --a | --b | --c
あなたはできます
complete -W '--a --b --c' foo
Tabを押すと表示される完了結果をカスタマイズすることもできます。空complete -E
たとえばcomplete -E -W 'foo bar'
。その後、空白のプロンプトでTabキーを押すだけでfoo
提案されますbar
。
コマンド補完をカスタマイズする方法いいえ- メッセージは空ですか?たとえば、と書くと、完了するようf
に完成をどのようにカスタマイズできますかfoo
?
(私が実際に欲しいのはloc
TAB→ですlocalc
。兄が私にこの質問をするように勧めましたが、彼はそれを望んでいますmplayer
。)
ベストアンサー1
とりわけ、コマンドの完了はbashを介して処理されます。行の完成を読む。これは、一般的な「プログラミング可能な完了」(コマンドが認識され、上記で識別された2つの特殊なケースでのみ呼び出されます)よりわずかに低いレベルで実行されます。
修正する:bash-5.0の新しいバージョン(2019年1月)はcomplete -I
この問題を正確に解決します。
関連するreadlineコマンドは次のとおりです。
complete (TAB) Attempt to perform completion on the text before point. Bash attempts completion treating the text as a variable (if the text begins with $), username (if the text begins with ~), hostname (if the text begins with @), or command (including aliases and functions) in turn. If none of these produces a match, filename completion is attempted. complete-command (M-!) Attempt completion on the text before point, treating it as a command name. Command completion attempts to match the text against aliases, reserved words, shell functions, shell builtins, and finally executable filenames, in that order.
より一般的な方法と同様に、complete -F
これらのいくつかはを使用して関数に渡すことができますbind -x
。
function _complete0 () {
local -a _cmds
local -A _seen
local _path=$PATH _ii _xx _cc _cmd _short
local _aa=( ${READLINE_LINE} )
if [[ -f ~/.complete.d/"${_aa[0]}" && -x ~/.complete.d/"${_aa[0]}" ]]; then
## user-provided hook
_cmds=( $( ~/.complete.d/"${_aa[0]}" ) )
elif [[ -x ~/.complete.d/DEFAULT ]]; then
_cmds=( $( ~/.complete.d/DEFAULT ) )
else
## compgen -c for default "command" complete
_cmds=( $(PATH=$_path compgen -o bashdefault -o default -c ${_aa[0]}) )
fi
## remove duplicates, cache shortest name
_short="${_cmds[0]}"
_cc=${#_cmds[*]} # NB removing indexes inside loop
for (( _ii=0 ; _ii<$_cc ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ -n "${_seen[$_cmd]}" ]] && unset _cmds[$_ii]
_seen[$_cmd]+=1
(( ${#_short} > ${#_cmd} )) && _short="$_cmd"
done
_cmds=( "${_cmds[@]}" ) ## recompute contiguous index
## find common prefix
declare -a _prefix=()
for (( _xx=0; _xx<${#_short}; _xx++ )); do
_prev=${_cmds[0]}
for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ "${_cmd:$_xx:1}" != "${_prev:$_xx:1}" ]] && break
_prev=$_cmd
done
[[ $_ii -eq ${#_cmds[*]} ]] && _prefix[$_xx]="${_cmd:$_xx:1}"
done
printf -v _short "%s" "${_prefix[@]}" # flatten
## emulate completion list of matches
if [[ ${#_cmds[*]} -gt 1 ]]; then
for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ -n "${_seen[$_cmds]}" ]] && printf "%-12s " "$_cmd"
done | sort | fmt -w $((COLUMNS-8)) | column -tx
# fill in shortest match (prefix)
printf -v READLINE_LINE "%s" "$_short"
READLINE_POINT=${#READLINE_LINE}
fi
## exactly one match
if [[ ${#_cmds[*]} -eq 1 ]]; then
_aa[0]="${_cmds[0]}"
printf -v READLINE_LINE "%s " "${_aa[@]}"
READLINE_POINT=${#READLINE_LINE}
else
: # nop
fi
}
bind -x '"\C-i":_complete0'
これにより、実行可能ファイルを生成できます~/.complete.d/
。たとえば、次のコマンドを使用する場合~/.complete.d/loc
:
#!/bin/bash
echo localc
これは(おおよそ)あなたが期待することを行います。
上記の関数は完璧ではありませんが、通常のbashコマンドの完了動作をある程度シミュレートします(特にsort | fmt | column
一致リストを表示する疑わしい繰越し)。
しかし、complete
これは大きな問題であり、メイン関数へのバインディングを置き換えるために関数のみを使用できます(デフォルトではTAB呼び出しを使用)。
このアプローチは、カスタムコマンドを完了するためのさまざまなキーバインディングとうまく機能しますが、それ以降の完全な完了ロジック(コマンドラインの後続の単語など)は許可されません。これを行うには、コマンドラインの解析、カーソル位置の処理、およびシェルスクリプトで考慮してはならない他のトリッキーな操作が必要です。