// Linux · 2025-12-18

Bash Shell 的展开与补全机制

1. Bash 展开机制的整体顺序

Bash 在处理命令行时,会按固定顺序执行多种展开(Expansions)。这一顺序至关重要,因为不同展开会相互影响。

步骤 展开类型 执行时机 关键说明
1 Brace Expansion(大括号展开) 最先执行 {a,b} → a b;支持序列 {1..10}
2 Tilde Expansion(波浪号展开)
Parameter/Variable Expansion(参数/变量展开)
Arithmetic Expansion(算术展开)
Command Substitution(命令替换)
同时执行,从左到右扫描 ~、$(...)、${var}、$((...)) 等在此阶段处理
- Process Substitution(进程替换) 与步骤 2 同时 <(...)、>(...)
3 Word Splitting(单词分割) 步骤 2 完成后 根据 $IFS 分割单词(默认空格、Tab、换行)
4 Filename Expansion(路径名展开/Globbing) 步骤 3 完成后 * ? [] 通配符匹配文件名
5 Quote Removal(引号移除) 最后执行 移除未转义的 \' " \

注意:

  • 双引号内抑制单词分割和路径名展开。
  • 单引号内抑制所有展开。
  • 引号移除总是最后一步,仅移除原始输入中的引号(展开产生的引号不移除,除非被转义)。

2. 主要展开类型详解

2.1 波浪号展开(Tilde Expansion)

在单词开头或赋值语句中,未加引号的 ~ 会展开为主目录路径。

形式 展开结果 示例
~ 当前用户主目录($HOME) cd ~ → 回家目录
~username 指定用户主目录(从 /etc/passwd 查询) ls ~root
~+ 当前工作目录($PWD) echo ~+
~- 上一个工作目录($OLDPWD) cd ~-(等价于 cd -)
~N(N 为数字) 目录栈第 N 项(dirs 显示) cd ~2
~/path 主目录下路径 cp file ~/backup
  • 与 cd - 结合:cd - 切换到上一个目录并打印路径(使用 $OLDPWD)。
  • 展开结果被视为已加引号,不会进一步分割或 glob。

2.2 参数与特殊参数展开

特殊参数 含义 示例
$_ 上一个前台管道或命令的最后一个参数(执行后设置) echo hello world → echo $_ 输出 world
!$ 通过历史展开,等价于上一个命令的最后一个参数 sudo !!$(历史展开形式)
!! 通过历史展开,等价于上一个完整命令 sudo !!
Alt + .(或 Esc + .) Readline 快捷键:插入上一个命令的最后一个参数(等价 $_)

其他常见特殊参数:

  • $?:上一个命令退出状态
  • $$:当前 Shell PID
  • $#:位置参数个数
  • $@ / $*:所有位置参数

2.3 历史展开(History Expansion)

默认在交互式 Shell 中启用(set -H 控制),以 ! 开头,在读取完整行后立即执行。

形式 含义 示例
!! 上一个完整命令 sudo !!
!n 历史第 n 条命令 !100
!-n 倒数第 n 条(!-1 = !!) !-2
!string 最近以 string 开头的命令 !ls
!?string? 最近包含 string 的命令
!^ 上一个命令第一个参数
!$ 上一个命令最后一个参数(同 $_ 在某些场景) mv !$ backup/
!* 上一个命令所有参数
!!:gs/old/new/ 上一个命令全局替换 old 为 new !!:gs/typo/correct/
:p 仅打印展开结果,不执行 !ls:p
^old^new^ 快速替换(非全局) ^cat^dog^
  • 建议启用 shopt -s histverify:展开后先显示,让你二次确认。
  • 在脚本中默认禁用,可用 set -H 开启。

2.4 其他展开简述

  • Brace Expansion:{a..z}、pre{a,b}post
  • Command Substitution:$(command) 或 `command`
  • Arithmetic Expansion:$((1+2))
  • Globbing:* ? [],可通过 shopt -s extglob 启用扩展模式 ?(pat)、*(pat) 等

3. Tab 补全机制(Programmable Completion)

Tab 补全是 Bash 最强大的交互工具,由 Readline 库实现,支持可编程补全。

基本行为

  • 单 Tab:唯一匹配时直接补全。
  • 双 Tab:列出所有可能匹配。
  • 默认补全:命令、文件名、变量、用户名、主机名等。

可编程补全机制

  • 使用 complete 内置命令定义 compspec。
  • 核心变量:COMPREPLY(数组,存放补全结果)。
  • 辅助工具:compgen(生成匹配列表)。
  • 常见选项:
    • -f / -d:文件名/目录
    • -c:命令
    • -v:变量
    • -u:用户名
    • -A function:调用自定义函数
  • 示例自定义补全函数:
    _myfunc() {
        COMPREPLY=( $(compgen -W "opt1 opt2 opt3" -- "$2") )
    }
    complete -F _myfunc mycommand

快捷键(Readline 默认绑定)

快捷键 功能
Tab 补全
M-Tab 菜单式补全(循环)
M-. / Alt+. 插入上一个命令最后一个参数(等价 $_ / !$)
M-* 插入所有匹配
M-? 列出所有匹配
Ctrl-R 反向历史搜索
  • 许多命令(如 git、systemctl)自带复杂补全脚本(来自 bash-completion 包)。

4. 实用技巧与建议

  • cd -:快速切换上/下目录。
  • Alt + .:快速引用上一个参数(比 !$ 更方便)。
  • Ctrl + R:历史反向搜索。
  • shopt -s cdable_vars:允许 cd varname 直接进入变量路径。
  • 启用 shopt -s direxpand:补全时直接展开变量。
  • 查看当前补全:complete -p <command>。

这些机制相互配合,让 Bash 命令行异常高效。熟练掌握展开顺序和历史/补全技巧,能显著减少重复输入。更多细节可以参考官方手册 man bash 中的 EXPANSION、HISTORY EXPANSION 和 PROGRAMMABLE COMPLETION 章节。

原文 https://blog.csdn.net/2301_79518550/article/details/156056334