效率工具

12 分钟阅读 · 16 组命令示例

文本搜索与处理指南:grep / ripgrep / sed / awk / 管道

适用场景:搜索代码内容、批量替换文本、日志过滤、数据提取、管道组合。

适用版本:ripgrep 14+、GNU grep/sed/awk;PowerShell 示例以 7+ 为主。

最后核对:2026-07-30

30 秒快速路径

bash
rg -n "TODO|FIXME" .
rg -l "oldImport" src/
rg --hidden --glob '!.git/**' "API_KEY" .

目录

  1. 核心概念
  2. grep — 基础文本搜索
  3. ripgrep (rg) — 现代替代品
  4. sed — 流式文本替换
  5. awk — 列提取与格式化
  6. 管道组合
  7. 正则表达式速查
  8. PowerShell 等价操作
  9. 实战场景

核心概念

管道思维

Unix 哲学:每个工具做一件小事,通过管道(|)串联成强大组合。

text
输入 → 过滤 → 变换 → 排序 → 去重 → 输出
bash
# 例:统计项目中哪个 npm 包被 import 最多
grep -rh "from '" src/ | sed "s/.*from '//;s/'.*//" | sort | uniq -c | sort -rn | head -10

标准流

编号 用途
stdin 0 输入(管道传入或文件)
stdout 1 正常输出
stderr 2 错误输出
bash
command > output.txt          # stdout 写入文件(覆盖)
command >> output.txt         # stdout 追加
command 2> error.log          # stderr 写入文件
command > all.log 2>&1        # stdout + stderr 合并写入
command > /dev/null 2>&1      # 丢弃所有输出

grep — 基础文本搜索

bash
# 基础搜索
grep "useState" src/App.tsx              # 搜索文件中包含 useState 的行
grep -r "TODO" ./src                     # 递归搜索目录
grep -rn "console.log" ./src             # -n 显示行号
grep -ri "error" ./logs                  # -i 忽略大小写
grep -rl "axios" ./src                   # -l 只列出文件名(不显示内容)

# 反向搜索
grep -v "node_modules" filelist.txt      # -v 排除匹配行

# 正则搜索
grep -E "import.*from" src/App.tsx       # -E 启用正则
grep -P "\d{3,}" log.txt                 # -P 是 GNU grep 扩展,macOS/BSD grep 通常不支持

# 上下文
grep -A 3 "error" log.txt               # -A 显示匹配行后 3 行
grep -B 2 "error" log.txt               # -B 显示匹配行前 2 行
grep -C 2 "error" log.txt               # -C 前后各 2 行

# 统计
grep -c "error" log.txt                  # 匹配行数
grep -rc "TODO" ./src                    # 每个文件的匹配数

ripgrep (rg) — 现代替代品

通常比递归 grep 更快,默认递归,并遵守 .gitignore;实际性能取决于文件数量、存储和搜索模式。默认还会跳过隐藏文件、二进制文件,并且不跟随符号链接,因此“搜不到”时先检查过滤规则。

bash
# 安装
# Windows: scoop install ripgrep / winget install BurntSushi.ripgrep
# macOS:   brew install ripgrep
# Linux:   apt install ripgrep

# 基础用法(自动递归当前目录)
rg "useState"                            # 搜索当前目录
rg "useState" src/                       # 指定目录
rg -i "error"                            # 忽略大小写
rg -l "axios"                            # 只列文件名
rg -c "TODO"                             # 每文件匹配数

# 过滤文件类型
rg -t ts "interface"                     # 只搜 .ts 文件
rg -t css "color"                        # 只搜 .css 文件
rg --type-list                           # 查看所有支持的类型
rg -g "*.tsx" "Props"                    # glob 过滤

# 调整默认过滤
rg --hidden "API_KEY"                      # 包含隐藏文件,仍遵守 ignore
rg --no-ignore "generated"                 # 不遵守 ignore 文件
rg -L "pattern"                            # 跟随符号链接;先确认不会形成大范围扫描

# 排除
rg --glob "!*.test.*" "function"         # 排除测试文件
rg --glob "!dist/**" "import"            # 排除 dist 目录

# 正则
rg "console\.(log|warn|error)"           # 默认就是正则
rg -F '$$'                               # 单引号避免 Bash 把 $$ 展开为当前 PID

# 替换预览(不修改文件)
rg "oldFunction" -r "newFunction"        # 显示替换后的样子

# JSON 输出(方便脚本处理)
rg --json "TODO"

sed — 流式文本替换

sed = Stream Editor,逐行处理文本。最常用于查找替换

bash
# 基础替换(输出到 stdout,不改原文件)
sed 's/old/new/' file.txt                # 每行替换第一个匹配
sed 's/old/new/g' file.txt               # g = 替换所有匹配(全局)
sed 's/old/new/gi' file.txt              # gi = 全局 + 忽略大小写

# 修改原文件前先保留备份并检查 diff
sed -i.bak 's/old/new/g' file.txt        # GNU/BSD sed 都会留下 file.txt.bak
diff -u file.txt.bak file.txt

# 删除行
sed '/pattern/d' file.txt                # 删除匹配行
sed '/^$/d' file.txt                     # 删除空行
sed '1d' file.txt                        # 删除第一行
sed '1,5d' file.txt                      # 删除 1-5 行

# 打印特定行
sed -n '10,20p' file.txt                 # 只打印 10-20 行
sed -n '/START/,/END/p' file.txt         # 打印两个标记之间的内容

# 插入/追加
sed '3i\new line here' file.txt          # 在第 3 行前插入
sed '3a\new line here' file.txt          # 在第 3 行后追加

# 分隔符可以换(路径里有 / 时有用)
sed 's|/usr/local|/opt|g' file.txt       # 用 | 做分隔符

# 多条规则
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt

awk — 列提取与格式化

awk 按行处理,擅长处理结构化文本(按空格/tab 分列)。

bash
# 基础列提取($1=第一列,$2=第二列,$0=整行)
awk '{print $1}' file.txt                # 打印第一列
awk '{print $1, $3}' file.txt            # 打印第一和第三列
awk '{print $NF}' file.txt               # 最后一列(NF=列数)

# 指定分隔符
awk -F: '{print $1}' /etc/passwd         # 以 : 分隔
awk -F, '{print $2}' data.csv            # 仅适合没有引号和嵌入逗号的简单数据

# 条件过滤
awk '$3 > 100' data.txt                  # 第三列大于 100 的行
awk '/error/' log.txt                    # 包含 error 的行(像 grep)
awk '$1 == "GET" {print $2}' access.log  # 第一列是 GET 的,打印第二列

# 计算
awk '{sum += $1} END {print sum}' nums.txt               # 求和
awk '{sum += $1; n++} END {print sum/n}' nums.txt        # 平均值

# 格式化输出
awk '{printf "%-20s %s\n", $1, $2}' file.txt             # 左对齐 20 字符宽

# 内置变量
# NR = 当前行号
# NF = 当前行的列数
# FS = 字段分隔符
awk 'NR > 1' data.csv                    # 跳过第一行(表头)
awk 'NR==5,NR==10' file.txt              # 打印 5-10 行

管道组合

常用组合工具

工具 功能
sort 排序
uniq 去重(需先排序)
wc 统计行数/字数/字符数
head / tail 取前/后 N 行
cut 按分隔符取列
tr 字符替换/删除
tee 同时输出到 stdout 和文件
xargs 将 stdin 转为命令参数

组合示例

bash
# 统计 src/ 下 .ts 文件数量
find src/ -name "*.ts" | wc -l

# 查找最大的 10 个文件(GNU find;路径中的空格不会被拆参数)
find . -type f -printf '%s\t%p\n' | sort -k1,1rn | head -10

# 去重并统计出现次数
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# → 统计访问最多的 IP

# 把搜索结果安全地传给另一个命令(GNU/Git Bash)
rg -l -0 "oldImport" src/ | xargs -0 -r sed -i.bak 's/oldImport/newImport/g'
# → 支持带空格的文件名,并保留 .bak;确认 diff 后再删除备份

# 同时看输出和保存到文件
npm run build 2>&1 | tee build.log

# CSV 提取某列
cut -d',' -f2 data.csv | sort -u
# → 提取第二列并去重

# 字符替换
echo "hello-world" | tr '-' '_'          # hello_world
echo "HELLO" | tr 'A-Z' 'a-z'           # hello
cat file.txt | tr -d '\r'               # 删除 Windows 换行符 \r

正则表达式速查

基础

不同工具使用的正则引擎不同。下表中的 POSIX ERE 写法适用于 grep -Esed -E 和大多数 awk;PCRE 专有语法应只在 grep -P、ripgrep 的 PCRE2 模式等明确支持它的工具中使用。

语法 含义 示例
. 任意单字符 a.c → abc, aXc
* 前面的 0 次或多次 ab*c → ac, abc, abbc
+ 前面的 1 次或多次 ab+c → abc, abbc
? 前面的 0 或 1 次 colou?r → color, colour
^ 行首 ^import
$ 行尾 \.tsx$
[[:digit:]] 数字 [[:digit:]]{3} → 123
[[:alnum:]_] 字母数字下划线 [[:alnum:]_]+
[[:space:]] 空白字符 [[:space:]]+
`(^ [^[:alnum:]_])` 可移植写法中用于模拟左侧单词边界

分组与引用

text
(pattern)          # 捕获组
(?:pattern)        # 非捕获组,仅 PCRE 等部分引擎支持
\1                 # 引用第一个捕获组

# sed 替换中使用捕获组
sed 's/\(.*\):\(.*\)/\2:\1/' file.txt    # 交换冒号前后
sed -E 's/(.*):(.*)/\2:\1/' file.txt     # -E 用现代语法

常用正则

用途 正则
邮箱(粗筛) [[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]+
IPv4(仅格式粗筛,不校验 0–255) [0-9]{1,3}(\.[0-9]{1,3}){3}
URL(粗筛) https?://[^[:space:]]+
汉字 不同引擎和 Unicode 版本差异较大;优先使用支持 Unicode Script 属性的工具
空行 ^$
import 语句 ^import.*from\s+['"](.+)['"]

PowerShell 等价操作

PowerShell 不用 grep/sed/awk,用对象管道 + cmdlet:

powershell
# grep → Select-String
Get-ChildItem -Path src -Recurse -File -Filter "*.ts" |
  Select-String -Pattern "useState"
Select-String -Path "log.txt" -Pattern "error" -CaseSensitive

# sed 替换 → -replace 操作符
Copy-Item -LiteralPath file.txt file.txt.bak
(Get-Content -LiteralPath file.txt) -replace 'old', 'new' |
  Set-Content -LiteralPath file.txt -Encoding utf8

# 批量替换
Get-ChildItem -Recurse -Filter "*.ts" | ForEach-Object {
  (Get-Content -LiteralPath $_.FullName) -replace 'oldImport', 'newImport' |
    Set-Content -LiteralPath $_.FullName -Encoding utf8
}

# awk 列提取 → Split + 索引
Get-Content log.txt | ForEach-Object { ($_ -split '\s+')[0] }

# sort | uniq -c → Group-Object
Get-Content log.txt | Group-Object | Sort-Object Count -Descending | Select -First 10

# wc -l → Measure-Object
(Get-Content file.txt | Measure-Object -Line).Lines

# head / tail
Get-Content file.txt -Head 20
Get-Content file.txt -Tail 10

# 管道到文件
command | Out-File output.txt
command | Tee-Object -FilePath output.txt    # 同时输出到屏幕和文件

批量改写前应先备份或确认版本控制状态,再检查 diff。PowerShell 7 的 utf8 是无 BOM UTF-8;Windows PowerShell 5.1 的同名参数会写 BOM,且文本改写可能统一换行符。需要保持原始编码或换行格式时,不要直接套用批量模板。


实战场景

1. 找出项目中所有 TODO/FIXME

bash
rg "TODO|FIXME|HACK|XXX" --glob "!node_modules/**"

2. 统计代码行数

bash
find src/ -type f \( -name "*.ts" -o -name "*.tsx" \) -print0 |
  xargs -0 -r wc -l | tail -1
# 或者用 cloc(专用工具)

3. 批量重命名文件中的 import 路径

bash
# 预览
rg "from '@/old-path" src/ -l

# 执行替换
rg -l -0 "from '@/old-path" src/ |
  xargs -0 -r sed -i.bak "s|from '@/old-path|from '@/new-path|g"

4. 从 JSON 日志中提取错误

bash
# 每行一个 JSON 对象(JSON Lines)时使用 jq,避免用正则解析 JSON
jq -r 'select(.level=="error") | .message' app.log

5. 查找尚未清理的 Git 冲突标记

bash
grep -rn "<<<<<<" src/     # 找出还有冲突标记的文件

6. 查看 package.json 中所有依赖版本

bash
jq -r '
  (.dependencies // {}) + (.devDependencies // {}) |
  to_entries[] | "\(.key)\t\(.value)"
' package.json | column -t

官方参考