路线图

文本变换与提取

星辉 2026-07-02 阅读 5 min 1,034 字 路线图
文本变换与提取 封面

Linux 运维基础 · 第七章 目标:掌握 cut/sort/uniq/sed/awk,能从日志和命令输出中精准提取数据


1. 概述#

上一章讲了"找",这一章讲"提取和变换"——找到之后怎么把有用的信息抠出来、排序、统计、格式化。这是运维"数据管道"的核心环节,也是面试高频考点。

工具链:cut(切列)→ sort(排序)→ uniq(去重统计)→ sed(替换/删除)→ awk(终极瑞士军刀)。


2. cut — 按列切割#

2.1 按分隔符切割#

bash
# -d 指定分隔符,-f 选字段(从 1 开始)
cut -d: -f1 /etc/passwd                  # 用户名(第 1 列)
cut -d: -f1,7 /etc/passwd               # 用户名 + Shell(第 1 和 7 列)
cut -d: -f1-3 /etc/passwd               # 第 1 到第 3 列

# 空格分隔
echo "192.168.1.1 8080 tcp" | cut -d' ' -f1   # 192.168.1.1

2.2 按字符位置切割#

bash
# -c 按字符位置
echo "2025-07-01 10:30:15" | cut -c1-10    # 2025-07-01
echo "ERROR:Connection timeout" | cut -c1-5  # ERROR

2.3 典型用法#

bash
# 列出所有用户名和 Shell
cut -d: -f1,7 /etc/passwd

# 从 ps 输出中只提取 PID 和命令
ps aux | cut -c1-20,66-

# 提取 Nginx 日志中的 IP
cut -d' ' -f1 /var/log/nginx/access.log

3. sort + uniq — 排序和去重#

3.1 sort — 排序#

bash
# 基础排序
sort file.txt                   # 字典序排序

# 数字排序
sort -n file.txt                # 按数值排序(不写 -n 的话 "10" < "2")
sort -nr file.txt               # 倒序数字排序
sort -rn file.txt               # 等价写法

# 其他常用参数
sort -u                         # 排序并去重(等效 sort | uniq)
sort -k2                        # 按第 2 列排序
sort -t: -k3 -n /etc/passwd    # 按冒号分隔的第 3 列(UID)数字排序
sort -t' ' -k2nr                # 按空格分隔的第 2 列数字倒序
sort -h file.txt                # 人类可读数字排序(4K < 2M < 1G,配合 du -h)

坑:locale 会悄悄改变排序结果。 现代系统默认是 UTF-8 locale,sort 的字典序会忽略大小写差异、标点,甚至把 aA 排在一起,导致 sort | uniq 去重不干净、脚本行为在不同机器上不一致。

bash
# 现象:UTF-8 locale 下大小写混排、标点被忽略
sort file.txt

# 要“按字节排序”(纯 ASCII 码顺序,可预测、更快):
LC_ALL=C sort file.txt
# 运维脚本、比对 diff、做精确去重时,强烈建议前面加 LC_ALL=C

3.2 uniq — 去重 / 统计#

bash
# 注意:uniq 只去除**连续**的重复行,所以通常先 sort
sort file.txt | uniq               # 去重
sort file.txt | uniq -c            # 去重 + 统计每行出现次数
sort file.txt | uniq -d            # 只显示重复的行
sort file.txt | uniq -u            # 只显示不重复的行

3.3 经典组合#

bash
# 访问量 Top10 IP
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
#                └ 提取IP   └ 排序  └ 统计计数  └ 按计数倒序 └ 取前10

# 每个 IP 访问了多少个不同 URI
cat access.log | awk '{print $1, $7}' | sort -u | awk '{print $1}' | sort | uniq -c | sort -rn

4. sed — 流编辑器#

sed非交互式 文本编辑工具,按行读、按规则处理、输出。可替换、删除、插入、提取某几行。

4.1 替换:s/old/new/#

bash
# 基本替换(每行第一次出现)
sed 's/old/new/' file.txt
echo "hello world" | sed 's/world/linux/'   # hello linux

# 全局替换(所有出现)
sed 's/old/new/g' file.txt

# 不同分隔符(当 old/new 里有 / 时换分隔符)
sed 's|/usr/local|/opt|' file.txt            # 用 | 代替 /
sed 's#http://#https://#' file.txt           # 用 # 更清晰

# 只替换第 N 次匹配
sed 's/old/new/2' file.txt                   # 替换每行第 2 次出现

# 指定行范围替换
sed '2,5s/old/new/' file.txt                # 只替换第 2-5 行
sed '/pattern/s/old/new/' file.txt           # 只替换匹配 pattern 的行

# 直接修改文件(加 -i 参数)
sed -i 's/old/new/g' file.txt               # 原地修改
sed -i.bak 's/old/new/g' file.txt           # 修改前备份为 file.txt.bak

4.2 删除行:d#

bash
sed '3d' file.txt                            # 删除第 3 行
sed '3,5d' file.txt                          # 删除第 3-5 行
sed '/pattern/d' file.txt                    # 删除匹配的行
sed '/^$/d' file.txt                         # 删除空行
sed '/^#/d' file.txt                         # 删除 # 开头(注释)的行

4.3 提取行范围:n,m p#

bash
# p 是打印,通常配合 -n(静默模式)使用
sed -n '5p' file.txt                         # 只打印第 5 行
sed -n '10,20p' file.txt                     # 打印第 10-20 行
sed -n '/start/,/end/p' file.txt            # 打印从 start 到 end 之间的所有行

4.4 插入和追加#

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

4.5 典型运维场景#

bash
# 修改 Nginx 配置:把 HTTP 重定向到 HTTPS
sed -i 's|listen 80;|listen 80;\n    return 301 https://$host$request_uri;|' nginx.conf

# 修改配置文件:切换数据库地址
sed -i 's/db_host=.*/db_host=10.0.1.100/' config.ini

# 清理日志:去掉 DEBUG 行
sed -i '/DEBUG/d' app.log

# 去掉行首尾空白
sed 's/^[ \t]*//;s/[ \t]*$//' file.txt

# 删除所有 # 注释行和空行(看有效配置)
sed '/^#/d;/^$/d' /etc/ssh/sshd_config

5. awk — 强大的文本处理语言#

awk 是运维排障的终极利器。按行读取、按列处理、支持条件过滤和计算。

5.1 基础语法#

bash
awk '条件 { 动作 }' file
# 条件可选,默认所有行
# 动作用 {} 包裹,默认 {print $0}(打印整行)

5.2 字段引用#

写法含义
$0整行
$1第 1 列(默认空格分隔)
$2第 2 列
$NF最后一列
$(NF-1)倒数第 2 列
NR当前行号
NF当前行的列数

5.3 常用场景#

bash
# 提取指定列
awk '{print $1, $3}' access.log                 # 第 1 和第 3 列

# 按条件过滤
awk '$3 > 100 {print $0}' data.txt              # 第 3 列大于 100 的行

# 按分隔符解析
awk -F: '{print $1, $3}' /etc/passwd            # -F 指定分隔符为 :

# 按行号过滤
awk 'NR>=5 && NR<=10' file.txt                  # 只处理 5-10 行

# 按正则过滤
awk '/ERROR/ {print $0}' app.log                 # 包含 ERROR 的行
awk '$1 ~ /^192\./ {print $0}' access.log        # 第 1 列匹配正则

# 程序块(BEGIN 和 END)
awk 'BEGIN {print "开始统计"} {sum+=$3} END {print "合计:", sum}' data.txt

5.4 运维实战#

实战 1:列出所有普通用户(UID >= 1000)#

bash
awk -F: '{if($3>=1000) print $1}' /etc/passwd
# 简写:
awk -F: '$3>=1000 {print $1}' /etc/passwd

实战 2:Top10 访问 IP#

bash
cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
#                                                              ^ IP 在第一列

实战 3:统计日志中每种 HTTP 状态码的数量#

bash
cat /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
# 200 184723
# 304  12345
# 404    567
# 500     23

实战 4:找非 Running 的 K8s Pod#

bash
kubectl get pods -A | awk '$4!="Running" {print $1, $2, $4}'
# NAMESPACE NAME STATUS

实战 5:计算访问日志总流量#

bash
# 假设第 10 列是响应大小(字节)
awk '{sum += $10} END {print "Total:", sum/1024/1024, "MB"}' access.log

实战 6:按时间段统计请求数#

bash
# 时间格式:[01/Jul/2025:10:30:15 +0800]
awk '{split($4, a, ":"); hour=a[2]; count[hour]++}
     END {for(h in count) print h":00", count[h]}' access.log | sort

实战 7:条件即模式 + 三元 if-else#

bash
# 技巧 1:把条件直接写在动作块前面,等价于 if,更简洁
awk -F: '{if($3>=1000) print $1}' /etc/passwd   # 啰嗦写法
awk -F: '$3>=1000 {print $1}'      /etc/passwd   # 等价简写(条件即模式)

# 技巧 2:真正的 if-else 用三元表达式,一行给每行打标签
awk -F: '{print $1, ($3>=1000 ? "普通用户" : "系统用户")}' /etc/passwd

6. 工具组合实战#

构造运维分析管道#

bash
# 场景:Nginx access.log,统计每个 IP 的最常访问 URI

awk '{a[$1 SUBSEP $7]++} END {          # 用 "IP\0URI" 作为 key 计数
       for(k in a) { split(k, p, SUBSEP); printf "%s %s %d\n", p[1], p[2], a[k] }
     }' access.log | sort -k1,1 -k3rn    # 按 IP + 访问次数排序

坑:a[$1][$7] 这种“真·多维数组”是 gawk 4.0+ 的扩展。 Debian/Ubuntu 默认 awkmawk,不支持多维数组,会直接报语法错误。跨发行版可移植的写法是上面这种 a[$1 SUBSEP $7](用内置分隔符 SUBSEP 把多个字段拼成一个 key),或干脆确认装了 gawk(Rocky/RHEL 默认就是 gawk)再用 a[i][j]

管道设计的思考方式#

  1. 提取awkcut 把需要的列拿出来
  2. 预处理sort 为下一步做准备
  3. 聚合uniq -cawk 做统计
  4. 排序展示sort -rn | head 取 TopN

7. 小结#

工具核心功能记忆口诀
cut按列切-d 分隔符 -f 列号
sort排序-n 数字 -r 倒序 -k 按列;精确去重加 LC_ALL=C
uniq去重统计只去相邻重复,必须先 sortuniq -c 计数
sed流编辑s/old/new/g 替换,d 删除,-i 原地修改
awk终极处理$1 第一列,$NF 最后一列,条件过滤 + 统计计算
管道组合数据流水线提取 → 排序 → 聚合 → 展示

上一章:六、文本查看与搜索 下一章:八、管道与重定向