命令的完成(以及其他操作)是通过bash readlinecomplete处理的。此操作的级别略低于通常的“可编程完成”(仅在识别了命令以及您在上面识别出的两种特殊情况下才调用)。
更新: 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
这将(大致)完成您的期望。
上面的函数虽然不完善(特别是sort | fmt | column
显示匹配列表的可疑随身携带品),但它在某种程度上模仿了正常的bash命令完成行为。
但是,与此有关的一个重要问题是它只能使用一个函数来替换对主complete
函数的绑定(默认情况下使用TAB调用)。
这种方法可以与仅用于自定义命令完成的其他键绑定一起很好地工作,但是此后它根本不实现完整的完成逻辑(例如,命令行中的后续单词)。这样做将需要解析命令行,处理光标位置以及其他在Shell脚本中可能不应该考虑的棘手问题...
loc
来localc
?我建议使用替代方法,因为经过一段时间的挖掘和搜索后,我还没有找到以这种方式自定义bash补全的方法。这可能是不可能的。