Bash命令补全自定义函数:当前补全词正则匹配不生效?
自定义Bash补全函数修复:Nginx配置激活脚本的补全逻辑问题
需求与问题
需求
- 输入
nginx-conf --<TAB>时,提示长选项:--help、--test、--testmode、--deactivate、--activate - 输入
nginx-conf <TAB>时,补全指定目录下的配置文件(匹配*.conf或*.conf.inactive格式)
问题
输入nginx-conf <TAB>时,仍会出现长选项,不符合预期的文件补全逻辑。
原代码
#!/bin/bash source "./common" _comp_nginx_conf(){ local IFS=$' ' local cur long_help conf_files new_help cur= long_help=( "help" "test" "testmode" "deactivate" "activate" ) # for correct completion, check if test mode is in the options for arg in "${COMP_WORDS[@]}"; do if [[ "$arg" == "--test" || "$arg" == "-t" ]]; then TEST_MODE=true fi done [[ $TEST_MODE ]] && cd "$TEST_PATH" &> /dev/null || cd "$PROD_PATH" &> /dev/null conf_files=$(ls -b *(.conf|.conf.incative) 2> /dev/null) new_help=() COMPREPLY=() cur="$2" if [[ $cur =~ "--".* ]]; then COMPREPLY=( $(compgen -W "${long_help[*]}" -P "--" -- $cur) ) else COMPREPLY=( $(compgen -W "${conf_files[*]}" -- $cur) ) fi return 0 } && complete -F _comp_nginx_conf nginx-conf
问题原因分析
- 文件名匹配拼写错误:
*.conf.incative应为*.conf.inactive,导致无法正确匹配目标配置文件,conf_files为空时,Bash会 fallback 到其他补全逻辑。 - 使用
ls获取文件列表风险:文件名含空格时会被拆分,且compgen -W会将空格作为分隔符,导致补全项混乱。 cd操作污染当前环境:直接切换目录会改变用户的当前工作目录,且若目录不存在会静默失败。- 变量初始化顺序错误:
cur先被设为空,后续才赋值,可能导致逻辑判断异常。 - 正则判断不够严谨:
[[ $cur =~ "--".* ]]的正则写法存在匹配漏洞,且未处理空输入的情况。
修复后的代码
#!/bin/bash source "./common" _comp_nginx_conf(){ # 开启扩展glob以支持复杂文件匹配 shopt -s extglob # 设置IFS为换行符,避免文件名含空格时被拆分 local IFS=$'\n' # 直接获取当前补全位置的输入内容 local cur="${COMP_WORDS[COMP_CWORD]}" local long_help=( "help" "test" "testmode" "deactivate" "activate" ) # 初始化测试模式为false,避免全局变量污染 local TEST_MODE=false local conf_files=() # 检查已输入的参数中是否包含测试模式选项 for arg in "${COMP_WORDS[@]:0:$COMP_CWORD}"; do if [[ "$arg" == "--test" || "$arg" == "-t" ]]; then TEST_MODE=true break fi done # 获取对应目录下的配置文件,不改变当前shell的工作目录 if $TEST_MODE; then conf_files=( "$TEST_PATH"/*.conf "$TEST_PATH"/*.conf.inactive ) else conf_files=( "$PROD_PATH"/*.conf "$PROD_PATH"/*.conf.inactive ) fi # 提取文件名(去掉路径前缀) conf_files=( "${conf_files[@]##*/}" ) # 过滤掉不匹配的项(当目录无对应文件时,glob会保留原路径字符串) conf_files=( "${conf_files[@]//!(*.conf|*.conf.inactive)}" ) COMPREPLY=() # 判断当前输入是否为长选项开头 if [[ "$cur" == --* ]]; then # 去掉已输入的--前缀,匹配长选项后再补全前缀 local cur_no_dash="${cur#--}" COMPREPLY=( $(compgen -W "${long_help[*]}" -- "$cur_no_dash" | sed 's/^/--/') ) else # 补全配置文件列表 COMPREPLY=( $(compgen -W "${conf_files[*]}" -- "$cur") ) fi # 关闭扩展glob,避免影响后续shell环境 shopt -u extglob return 0 } && complete -F _comp_nginx_conf nginx-conf
关键修复点说明
- 避免目录切换:直接通过路径拼接获取文件,不改变用户当前工作目录,更安全。
- 正确匹配文件名:修正拼写错误,用数组存储文件列表,避免空格拆分问题。
- 严谨的长选项判断:用
== --*替代正则,更直观且无匹配漏洞。 - 局部变量隔离:所有变量设为局部,避免污染全局环境。
- 过滤无效项:处理目录无对应文件的情况,避免补全错误的路径字符串。
内容的提问来源于stack exchange,提问作者Xalares
相关产品推荐
相关产品推荐

