个人技术站点JASONWU

Keep Coding


  • 主页

  • 文章

  • 搜索

Bash 命令行提效

新建: 2025-07-28 编辑: 2025-08-07   |   分类: DevOps   | 字数: 3114 字

终端快捷键

操作快捷键
跳到行首Ctrl + A
跳到行尾Ctrl + E
向前一个字符Ctrl + F / →
向后一个字符Ctrl + B / ←
向前一个单词Alt + F
向后一个单词Alt + B
删除到行首Ctrl + U
删除到行尾Ctrl + K
删除光标前一个单词Ctrl + W
删除光标后一个单词Alt + D
删除光标所在字符Ctrl + D
粘贴上次删除内容Ctrl + Y
撤销操作Ctrl + _
上一条历史命令Ctrl + P / ↑
下一条历史命令Ctrl + N / ↓
清屏Ctrl + L
快速参数补全Tab

历史命令查询与复用

💡 反向搜索历史命令(Ctrl + R)不好用,更推荐使用 history 命令配合 grep 进行搜索:

Bash
1# 1. 批量查看所有包含 "ssh ku" 的历史命令编号和内容
2history | grep -i "ssh ku"
3
4# 2. 执行指定编号为 n 的历史命令
5!n
6
7# 或打印出指定编号的历史命令内容
8!n:p # 之后可通过「上一条历史命令」恢复历史命令并编辑

此外感叹号 ! 也可以用来执行最近的命令:

Bash
1# 执行上一次输入的完整命令
2!!
3
4# 重新以 sudo 权限运行上一条命令
5sudo !!

⚠️ ! 表达式执行的命令会立即运行,带有风险,建议先用 history 确认要复用的历史命令内容再使用。

多会话一致性与同步机制

默认情况下,Bash 的历史命令 history 不会在不同会话(session)之间实时同步。每个 Bash 会话都维护各自独立的内存缓存,只有在特定操作发生时才会与磁盘上的主历史文件(例如 ~/.bash_history)进行读写操作:

  • 写入主历史文件
    • 当关闭当前 session,Bash 会把此会话的历史命令追加或写入到 ~/.bash_history
    • 手动执行 history -a(append,推荐 ✅)可立即把当前 session 的历史追加到主文件
    • 或 history -w(write,覆盖,不推荐 ⚠️)会直接覆盖主文件,可能导致历史丢失
  • 读取主历史文件
    • 新开一个 session 时,Bash 会读取已有历史文件作为初始命令历史
    • 若当前 session 需要看到其他 session 新增的历史命令,需要主动执行 history -r(reload,重新读取)来同步

通配符高效操作文件

Globbing:源自 Unix 早期命令 glob(取自 global 的缩写),后来演变为动词 globbing,意指 Shell 对通配符进行的路径名展开。

Wildcard vs Globbing

  • Wildcard(通配符):指代用于匹配文件名或路径的特殊字符,如 *、?、[] 等
  • Globbing(模式匹配):Shell 检测并展开 wildcard 模式的过程,又称「路径名展开(pathname expansion)」

两词在日常口语里常互换使用,但严格来说 wildcard 是原材料,而 globbing 则是加工动作。

⚠️ 与正则表达式区别

Shell globs ≠ 正则表达式(regular expression,简写为 regex)。

  • Glob 的星号 * 匹配「任意长度、任意字符」
  • Regex 的 * 则是「重复前一个原子 0 次或多次」

在正则表达式领域,atom(原子)并不是普通化学用语的「原子」,而是一个专门术语,指「一个可以被量词(如 *、+、?)作用的最小匹配单元」

  • 一个原子可以是:
    • 单个普通字符,例如 a
    • 一个转义字符,例如 \d(等价于 [0-9])
    • 一个字符类,如 [A-Z]
    • 一个括号分组,例如 (abc)
    • 一个断言,如 ^ 或 \b

大括号 {}:批量枚举或批量生成。

Bash
1touch file{w1,w2,w3,x1,x2,x3}{.txt,.log}

创建 filew1.txt、filew1.log、filew2.txt、filew2.log 等文件。

星号 *:匹配任意数量的任意字符(包括零个)。

Bash
1ll file*.txt

列出所有以 file 开头,后面跟任意字符并以 .txt 结尾的文件,如 filew1.txt、filew2.txt 等。

问号 ?:匹配任意单个字符。

Bash
1ll file?1.txt

列出所有以 file 开头,后面跟一个任意字符并以 1.txt 结尾的文件,如 filew1.txt、filex1.txt 等。

方括号 []:匹配括号内的任意一个字符。

Bash
1ll file[wx][13].txt

列出所有以 file 开头,后面跟 w 或 x,再跟 1 或 3 并以 .txt 结尾的文件,如 filew1.txt、filex3.txt 等。

感叹号 ! 和插入符 ^:[!abc] 或 [^abc] 匹配除了 a、b、c 之外的任意单个字符。

Bash
1ll file*[^13].*

列出所有以 file 开头,后面跟任意字符但不包含 1 或 3 的文件,如 filew2.txt、filex2.log 等。

使用技巧与注意事项

  • 通配符通常与 ls、cp、mv、rm 等命令组合,实现批量文件操作
  • 通配符只在命令参数中生效,Shell 会自动扩展匹配的文件名。如果没有匹配项,表达式会被当作文本处理
  • 通配符不能被引号包裹,因为引号会「阻止 Shell 提前展开」通配符
    • 比如:ll "*.txt" 会列出名为 *.txt 的文件,而不是所有 .txt 文件
  • * 不会匹配以点(.)开头的隐藏文件,除非开启让通配符匹配隐藏文件的功能(shopt -s dotglob)
    • 关闭让通配符匹配隐藏文件的功能:shopt -u dotglob
  • 可以用反斜杠 \* 或引号禁用通配特性,使其按普通字符处理

高效查找文件

find vs locate

  • find:实时遍历磁盘,支持复杂条件与批量操作,速度受目录规模影响,结果最准确
    • 使用场景:准确性、实时性或复杂操作
  • locate:查询预先索引的数据库(sudo updatedb),几乎瞬时返回文件名匹配结果,但需等待索引更新,功能相对单一
    • 使用场景:速度优先且只查文件名

按名称快速定位文件

Bash
1# 精确匹配(大小写敏感)
2find . -name "config.toml"
3
4# 模糊匹配且忽略大小写
5find . -iname "*time*.html"

为什么 find . -iname "*time*.html" 可以把通配符放在引号里? 🤔 ✨

关键在于理解:Shell 先展开与程序后匹配的区别。

流程详解:

步骤未加引号 *time*.html加引号 "*time*.html"
1. Shell 接收命令行立即在当前目录做文件名展开(globbing),如果正好存在 readtime.html 等文件,Shell 会把它们列成参数。若不存在匹配文件,Shell 原样保留 *time*.html因为有引号,Shell 不做任何展开,参数保持字面量 *time*.html
2. 调用 find 程序可能得到一长串已展开的文件名,也可能得到 *time*.html(取决于第 1 步结果)始终得到 *time*.html 这一模式字符串
3. find 解析 -name/-iname 模式如果收到的是文件名列表,find 会把它们当作多个独立表达式,往往导致语法错误或者仅在当前目录生效,结果预期出路巨大find 按设计使用通配符去递归匹配每个文件的「基名」,得到正确结果

结论:想让 find 自己处理通配符,就必须阻止 Shell 抢先展开——最简单方式就是用单/双引号或反斜杠 \* 进行引用。

查空文件或目录

Bash
1# 查空文件/目录
2find . -empty

按时间维度查文件

Bash
1# 查找最近 2 小时(=120 分钟)内被修改过的文件
2find . -type f -mmin -120
3
4# 查找 2 小时到 4 小时之间(120~240 min)改动的文件
5find . -type f -mmin +120 -mmin -240
6
7# 查找比 `README.md` 内容修改时间更晚的文件
8find . -newer README.md

Linux 文件有三种主要时间戳:

  • 修改时间(modify time):文件内容最后被更改的时间
    • -mtime:按天查找文件修改时间
    • -mmin:按分钟查找文件修改时间
    • -newer:实际只比较 mtime
  • 访问时间(access time):上次读取文件内容的时间(如用 cat 查阅,但没改动内容)
    • -atime:按天查找文件访问时间
    • -amin:按分钟查找文件访问时间
  • 状态改变时间(change time):属性变化时更新时间,例如权限、所有者变化,或者 mtime 被更新时
    • -ctime:按天查找文件状态改变时间(如权限变更
    • -cmin:按分钟查找文件状态改变时间(如权限变更)
  • 比如:
    • 文件新建时,atime/mtime/ctime 都等于创建时间
    • 保存内容修改,mtime 和 ctime 都更新
    • 仅 chmod 改权限,只有 ctime 更新

避免递归遍历查询更快

Bash
1# 仅列当前目录的 `.log` 文件
2find . -maxdepth 1 -type f -name "*.log"
3
4# 统计当前目录中 Java 文件数量
5find . -maxdepth 1 -type f -name "*.java" | wc -l
6
7# 搜索当前目录中 `文件大小 > 10 MB` 的文件
8find . -maxdepth 1 -type f -size +10M

按内容搜源码

Bash
1# 排除除任意层级下的 `.venv` 目录,再在 `.py` 文件中查 `woff2` 相关内容
2find . -path "*/.venv" -prune -o -type f -name "*.py" -exec grep -nH "woff2" {} +
3
4# 排除除任意层级下的 `dist` 或 `.venv` 目录,再在 `.py` 文件中查 `woff2` 相关内容
5find . \( -path "*/dist" -o -path "*/.venv" \) -prune -o -type f -name "*.py" -exec grep -nH "woff2" {} +
6
7# 或使用 GNU `grep` 代替会更简洁
8grep -rnH --include="*.py" --exclude-dir={dist,.venv} "woff2" .

ps 与 grep 命令

ps 命令用于显示系统中当前运行的进程信息,grep 命令用于在文本中查找匹配的内容,两者经常结合使用(通过管道符 |)来方便地搜索和筛选出特定的进程信息。比如列出所有包含 "nginx" 关键字的进程:

Bash
1ps aux | grep -i "nginx"

✨ 由于 grep 本身也会被 ps 输出(因为执行了 grep 进程),要避免这一行可以用如下写法:

Bash
1ps aux | grep -i "[n]ginx" 

这样 grep 只会匹配 nginx 进程,而不会把自身(grep nginx)带入匹配。

#Bash# #通配符#

文章:Bash 命令行提效

链接:https://www.wuxianjie.net/posts/devops/bash-shortcuts/

作者:吴仙杰

文章: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议,转载请注明出处!

Python 环境管理一体化:uv VS pip/venv
JavaScript Fetch API:用法、示例与最佳实践
  • 文章目录
  • 站点概览
吴仙杰

吴仙杰

🔍 Ctrl+K / ⌘K

27 文章
9 分类
25 标签
邮箱 GitHub
  • 终端快捷键
  • 历史命令查询与复用
    • 多会话一致性与同步机制
  • 通配符高效操作文件
  • 高效查找文件
    • 按名称快速定位文件
    • 查空文件或目录
    • 按时间维度查文件
    • 避免递归遍历查询更快
    • 按内容搜源码
  • ps 与 grep 命令
© 2021-2025 吴仙杰 保留所有权利 All Rights Reserved
浙公网安备 33010302003726号 浙ICP备2021017187号-1
0%