理解Bash自动补全机制
Bash的自动补全功能通过complete
内置命令和补全函数实现。典型的补全函数结构如下:
function _mycommand() {
local cur prev
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# 补全逻辑
if [[ $COMP_CWORD -eq 1 ]]; then
COMPREPLY=( $(compgen -W "arg1 arg2" -- "$cur") )
fi
}
complete -F _mycommand mycommand
多命令输出合并补全
要实现同时补全pwd
和hostname
的输出,我们可以这样修改补全函数:
function _command() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
case $COMP_CWORD in
1)
# 合并pwd和hostname输出
local pwd_output=$(pwd)
local hostname_output=$(hostname)
COMPREPLY=( $(compgen -W "$pwd_output $hostname_output" -- "$cur") )
;;
*)
# 其他位置的补全逻辑
COMPREPLY=( $(compgen -f -- "$cur") )
;;
esac
}
complete -F _command command
集成文件路径补全
要在补全中同时支持文件路径,可以使用compgen -f
选项:
function _advanced_complete() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
# 获取命令输出
local cmd_outputs="$(pwd) $(hostname)"
# 合并命令输出和文件补全
COMPREPLY=(
$(compgen -W "$cmd_outputs" -- "$cur")
$(compgen -f -- "$cur")
)
# 去重
COMPREPLY=($(printf "%s\n" "${COMPREPLY[@]}" | sort -u))
}
complete -F _advanced_complete mycmd
实际应用示例
下面是一个更完整的示例,支持多级参数和不同类型的补全:
function _smart_complete() {
local cur prev
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case $prev in
--path|-p)
# 只补全目录
COMPREPLY=( $(compgen -d -- "$cur") )
;;
--host|-h)
# 补全主机名相关
COMPREPLY=( $(compgen -W "$(hostname) $(hostname -f) $(hostname -s)" -- "$cur") )
;;
*)
# 默认补全:命令选项+当前目录
local options="--help --path --host"
COMPREPLY=(
$(compgen -W "$options" -- "$cur")
$(compgen -f -- "$cur")
)
;;
esac
}
complete -F _smart_complete smartcmd
性能优化建议
当处理大量补全项时,可以考虑以下优化:
function _optimized_complete() {
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
# 使用数组而非字符串拼接
local -a completions
completions+=($(pwd))
completions+=($(hostname))
completions+=($(compgen -f -- "$cur"))
# 使用关联数组去重
local -A unique
for item in "${completions[@]}"; do
unique["$item"]=1
done
COMPREPLY=("${!unique[@]}")
}
错误处理与边界情况
完善的补全函数应该处理各种边界情况:
function _robust_complete() {
local cur=${COMP_WORDS[COMP_CWORD]}
# 检查命令是否存在
if ! command -v hostname &>/dev/null; then
COMPREPLY=($(compgen -f -- "$cur"))
return
fi
# 处理带空格的路径
local pwd_output
pwd_output=$(pwd | sed 's/ /\\ /g')
# 安全合并输出
COMPREPLY=()
while IFS= read -r item; do
COMPREPLY+=("$item")
done < <(compgen -W "$pwd_output $(hostname)" -- "$cur" 2>/dev/null)
# 添加文件补全
mapfile -t files < <(compgen -f -- "$cur" 2>/dev/null)
COMPREPLY+=("${files[@]}")
}