shell 历史记录技巧
· 8 min read
常用技巧:
fc -l -10 # 查看最近执行的10条命令
fc -l -5 | grep npm # 从最近执行的5条命令中搜索包含 npm 的命令
!npm # 执行最近一次执行的以 `npm` 开头的命令
!?git? # 执行最近一次执行的包含 `git` 的命令
!python:p # 显示最近一次执行的包含 `python` 的命令,但不执行
!?dev?:p # 显示最近一次执行的包含 `dev` 的命令,但不执行
# 注意如果要查找的最后一次使用的命令中间有空格,只能使用 !?string? 形式,比如 !?npm install?
fc 命令
fc 命令(Fix Command)主要用于从历史记录中调出命令进行编辑后执行,或直接重新执行并附带简单的字符串替换。
以下是它的常用语法分类:
📝 语法一:列出与编辑历史命令
基础格式:fc [-e 编辑器名] [-lnr] [起始] [结束]
常用选项:
-l:列出命令而不是编辑执行。这是最常用的查看选项。-n:列出时隐藏命令前面的编号。-r:反转列表的顺序。-e:指定编辑命令时使用的编辑器(如vim)。
范围指定([起始] / [结束]):
- 可以是数字(如
fc -l 100 110)或字符串(匹配最近以此开头的命令)。 - 支持负数偏移:
fc -l -5表示列出最近 5 条命令。 - 若省略
结束:在列出时默认为当前命令;若省略起始:列出时默认为 -16,编辑时默认为上一条命令。
🔄 语法二:替换并重新执行
基础格式:fc -s [旧字符串=新字符串] [命令]
-s:代表substitute(替换)。它会重新执行指定的命令,并在其中进行字符串替换。- 常用别名:官方文档建议设置别名
alias r='fc -s'。这样输入r就重复上条命令,输入r cc就会执行最近一条以cc开头的命令。
💡 实用示例
- 查看最近 10 条:
fc -l -10 - 直接编辑并执行上一条:
fc(默认调用 vi) - 替换并重跑:
fc -s old=new(把上条命令里的 old 换成 new 后执行)
如果你对某个具体选项(比如编辑器设置)的用法感兴趣,我们可以继续深入。
! 扩展字符
! 是 Bash 历史扩展的触发字符,它本身不是一个独立命令,而是一套用来引用历史记录中命令的语法。下面从“事件指示器”(选哪条命令)和“单词指示器”(选命令的哪部分)两个层面来详细介绍。
事件指示器 (Event Designators)
这是 ! 语法中最核心的部分,用于从历史列表中选择一条命令。
| 语法 | 含义 | 示例 |
|---|---|---|
!n | 引用历史列表中第 n 条命令 | !100 执行第 100 条命令 |
!-n | 引用倒数第 n 条命令 | !-3 执行倒数第 3 条命令 |
!! | 引用上一条命令(!-1 的简写) | sudo !! 用 sudo 重新执行上条命令 |
!string | 引用最近一条以 string 开头的命令 | !git 执行最近一条 git 开头的命令 |
!?string? | 引用最近一条包含 string 的命令(尾部的 ? 可省略) | !?nginx? 执行最近一条含 nginx 的命令 |
^string1^string2^ | 快速替换:重复上条命令,并把 string1 替换为 string2 | ^test^prod^ 把上条命令里的 test 改成 prod 后执行 |
!# | 引用当前行到目前为止已输入的内容 | 常用于编辑场景 |
单词指示器 (Word Designators)
选出命令后,还可以用 : 加指示器来选取命令中的特定部分(如参数)。
| 语法 | 含义 | 示例 |
|---|---|---|
!^ | 上条命令的第一个参数 | 上条是 ls -la /tmp,!^ 得到 -la |
!$ | 上条命令的最后一个参数 | 上条是 cp a.txt b.txt,!$ 得到 b.txt |
!* | 上条命令的所有参数(不含命令本身) | 快速复用上条命令的所有参数 |
!:n | 上条命令的第 n 个单词(从 0 开始计数) | !:2 取上条命令的第 2 个参数 |
!:x-y | 上条命令的第 x 到 y 个单词的范围 | !:1-3 取第 1 到第 3 个参数 |
常用修饰符 (Modifiers)
在单词指示器后,还可以加 : 修饰符对选中的内容做进一步处理。
| 修饰符 | 含义 |
|---|---|
:p | 只打印展开后的命令,不执行(非常实用,避免误操作) |
:h | 去掉路径的尾部文件名,只保留头部目录 |
:t | 只保留路径的尾部文件名 |
:r | 去掉文件后缀 |
:s/old/new/ | 把命令中的 old 替换为 new(只替换第一个) |
:gs/old/new/ | 全局替换 |
:q | 对替换后的单词加引号,防止进一步展开 |
实用示例
# 1. 先查看再执行(推荐习惯)
!git:p # 只打印最近一条 git 命令,不执行
# 2. 用上条命令的最后一个参数
ls /etc/nginx/
cd !$ # 等价于 cd /etc/nginx/
# 3. 快速修正上条命令的拼写错误
# 上条命令:grep "error" /var/log/syslog
^syslog^messages^ # 修正为 grep "error" /var/log/messages
# 4. 用 sudo 重跑上条命令
sudo !!
# 5. 替换上条命令中的字符串
# 上条:python test.py
^test^main^ # 执行 python main.py
注意事项
- 默认开启:交互式 Bash 默认启用历史扩展,非交互式 shell 默认关闭。
- 转义:如果
!后面紧跟空格、制表符、换行、=或 shell 元字符,它不会被当作历史扩展的触发符。 - 单引号保护:在单引号内,
!不会被历史扩展处理;在双引号内,!仍然可能触发扩展(除非用\转义)。