der*_*ert 24 bash autocomplete
在 中bash,使用complete内置命令设置自定义完成命令参数非常容易。例如,对于一个具有以下概要的假设命令
foo --a | --b | --c
Run Code Online (Sandbox Code Playgroud)
你可以
complete -W '--a --b --c' foo
Run Code Online (Sandbox Code Playgroud)
当你按你还可以自定义你完成Tab在空提示符下使用complete -E,例如complete -E -W 'foo bar'。然后,在空提示下按 Tab 只会建议foo和bar。
如何在非空提示下自定义命令完成?例如,如果我写f,我如何自定义完成以使其完成foo?
(我想,实际情况是locTAB?localc。而我的兄弟,谁促使我提出这一点,与希望它mplayer。)
mr.*_*tic 10
命令的完成(以及其他事情)是通过 bash readline completion处理的。这比通常的“可编程完成”(仅在识别命令时调用,以及您在上面识别的两种特殊情况下调用)的级别略低。
更新: bash-5.0 的新版本(2019 年 1 月)complete -I正好解决了这个问题。
相关的 readline 命令是:
Run Code Online (Sandbox Code Playgroud)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'
Run Code Online (Sandbox Code Playgroud)
这将启用您自己的每个命令或前缀字符串挂钩~/.complete.d/。例如,如果您使用以下命令创建可执行文件~/.complete.d/loc:
#!/bin/bash
echo localc
Run Code Online (Sandbox Code Playgroud)
这将(大致)满足您的期望。
上面的函数在一定程度上模拟了正常的 bash 命令完成行为,尽管它是不完美的(尤其是sort | fmt | column显示匹配列表的可疑随身携带)。
然而,一个重要的问题是它只能使用一个函数来替换与主complete函数的绑定(默认情况下使用 TAB 调用)。
这种方法适用于仅用于自定义命令完成的不同键绑定,但在此之后它根本不会实现完整的完成逻辑(例如,命令行中的后面的单词)。这样做需要解析命令行、处理光标位置以及其他在 shell 脚本中可能不应该考虑的棘手事情......