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章节。