bash
では、complete
ビルトインを使用してコマンド引数のカスタマイズされた補完を設定するのは簡単です。たとえば、あらすじのある架空のコマンドの場合
foo --a | --b | --c
あなたはできる
complete -W '--a --b --c' foo
を押したときに得られる補完をカスタマイズすることもできます Tab at emptyを使用したプロンプトcomplete -E
、 例えば complete -E -W 'foo bar'
。次に、空のプロンプトでタブを押すと、foo
とbar
のみが提案されます。
non-emptyプロンプトでコマンド補完をカスタマイズするにはどうすればよいですか?たとえば、f
と書いた場合、foo
まで補完するように補完をカスタマイズするにはどうすればよいですか?
(私が望む実際のケースはloc
TAB →localc
。そして、私にこれを尋ねるように促した兄は、mplayer
でそれを望んでいます。)
コマンドの完了(および他のもの)は、bashreadline completionを介して処理されます。これは、通常の「プログラム可能な完了」(コマンドが特定された場合にのみ呼び出される、および上記で特定した2つの特殊なケース)よりも少し低いレベルで動作します。
pdate: 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で呼び出されます)。
このアプローチは、カスタムコマンド補完だけに使用される別のキーバインドでうまく機能しますが、その後の完全な補完ロジック(たとえば、コマンドラインの後半の単語)を実装しないだけです。これを行うには、コマンドラインの解析、カーソル位置の処理、およびおそらくシェルスクリプトでは考慮すべきではないその他のトリッキーなことを行う必要があります...
あなたのための簡単な答えは
$ cd into /etc/bash_completion.d
$ ls
基本的な出力だけ
autoconf gpg2 ntpdate shadow
automake gzip open-iscsi smartctl
bash-builtins iconv openssl sqlite3
bind-utils iftop Perl ssh
brctl ifupdown pkg-config strace
bzip2 info pm-utils subscription-manager
chkconfig ipmitool postfix tar
configure iproute2 procps tcpdump
coreutils iptables python util-linux
cpio lsof quota-tools wireless-tools
crontab lvm redefine_filedir xmllint
cryptsetup lzma rfkill xmlwf
dd make rpm xz
dhclient man rsync yum.bash
e2fsprogs mdadm scl.bash yum-utils.bash
findutils module-init-tools service
getent net-tools sh
目的のプログラムを自動補完してbash補完に追加するだけです
私がこれに対するあなたの必要性を理解していたかどうかわかりません...
これは、bashがf
で始まる1つのコマンドしか認識していないことを意味します。
補完の基本的な考え方は、あいまいな場合は可能性を印刷することです。
そのため、PATH
をこの1つのコマンドのみを含むディレクトリに設定し、すべてのbashビルトインを無効にしてこの作業を行うことができます。
とにかく、私はあなたに一種の回避策を与えることもできます:
alias _='true &&'
complete -W foo _
したがって、_ <Tab>
と入力すると、_ foo
まで完了し、foo
が実行されます。
それでもalias f='foo'
の方がはるかに簡単です。