跳转至

文本处理三剑客

文本处理三剑客

📌 学习时间: 4-5 天 📌 难度级别:⭐⭐⭐ 中级 📌 前置知识:文件与目录管理、基础正则表达式


📚 章节概述

grep 、 sed 、 awk 被称为 Linux 文本处理「三剑客」,是 Shell 编程和日常运维中最强大的工具。本章将深入讲解正则表达式、三剑客的完整用法,以及 sort/uniq/cut/paste/tr/wc 等辅助工具,配合大量日志分析和数据提取的实战案例。

🎯 学习目标

  • 精通正则表达式(基础正则 + 扩展正则)
  • 熟练使用 grep 进行文本搜索
  • 掌握 sed 流编辑器的完整用法
  • 掌握 awk 模式匹配与数据处理
  • 熟练使用 sort/uniq/cut/paste/tr/wc 等工具
  • 深入理解管道和重定向机制
  • 能独立完成日志分析和数据提取任务

📖 1. 正则表达式详解

1.1 基础正则表达式( BRE )

Bash
# 字符匹配
.         # 匹配任意单个字符
[abc]     # 匹配 a、b 或 c 中的任一个
[a-z]     # 匹配 a 到 z 中的任一个
[0-9]     # 匹配数字
[^abc]    # 匹配非 a、b、c 的任一字符
[^0-9]    # 匹配非数字字符

# 锚定
^         # 匹配行首
$         # 匹配行尾
^$        # 匹配空行
\b        # 匹配单词边界
\<        # 匹配单词开头
\>        # 匹配单词结尾

# 量词(BRE 中需要转义)
*         # 前一个字符出现 0 次或多次
\+        # 前一个字符出现 1 次或多次
\?        # 前一个字符出现 0 次或 1 次
\{n\}     # 前一个字符出现 n 次
\{n,\}    # 前一个字符出现至少 n 次
\{n,m\}   # 前一个字符出现 n 到 m 次

# 分组与反向引用
\(pattern\)   # 分组
\1 \2         # 反向引用第 1、2 个分组

1.2 扩展正则表达式( ERE )

Bash
# ERE 中量词不需要转义(使用 grep -E 或 egrep)
+         # 1次或多次
?         # 0次或1次
{n}       # 恰好n次
{n,}      # 至少n次
{n,m}     # n到m次

# 交替
|         # 或(cat|dog 匹配 cat 或 dog)

# 分组
(pattern) # 分组(不需要转义)
\1        # 反向引用

# POSIX 字符类
[:alpha:]   # 字母 [a-zA-Z]
[:digit:]   # 数字 [0-9]
[:alnum:]   # 字母和数字
[:space:]   # 空白字符
[:upper:]   # 大写字母
[:lower:]   # 小写字母
[:punct:]   # 标点符号

# 使用方式
grep '[[:digit:]]' file.txt      # 包含数字的行
grep '[[:upper:]][[:lower:]]' f  # 大写后跟小写

1.3 正则表达式实例

Bash
# 匹配 IP 地址(简化版)
grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file

# 匹配邮箱
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' file

# 匹配手机号(中国大陆)
grep -E '1[3-9][0-9]{9}' file

# 匹配日期 (YYYY-MM-DD)
grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' file

# 匹配 URL
grep -E 'https?://[a-zA-Z0-9./?=_-]+' file

# 匹配空行
grep '^$' file

# 匹配注释行(# 开头,允许前导空格)
grep -E '^\s*#' file

# 匹配非空非注释行
grep -vE '^\s*(#|$)' file

📖 2. grep — 文本搜索

2.1 基本用法

Bash
# 基本语法:grep [选项] 模式 文件

# 基本搜索
grep "error" /var/log/syslog         # 搜索包含 error 的行
grep "error" file1 file2 file3       # 在多个文件中搜索
grep "error" /var/log/*.log          # 通配符搜索

# 常用选项
grep -i "error" file               # 忽略大小写
grep -v "debug" file               # 反向匹配(排除包含 debug 的行)
grep -n "error" file               # 显示行号
grep -c "error" file               # 只显示匹配的行数
grep -l "error" /var/log/*.log     # 只显示文件名
grep -L "error" /var/log/*.log     # 显示不匹配的文件名
grep -w "error" file               # 全词匹配(不匹配 errors)
grep -x "exact line" file         # 整行匹配
grep -r "error" /var/log/         # 递归搜索目录
grep -rn "TODO" ./src/            # 递归搜索 + 行号

# 上下文显示
grep -A 3 "error" file             # 显示匹配行后3行(After)
grep -B 3 "error" file             # 显示匹配行前3行(Before)
grep -C 3 "error" file             # 显示匹配行前后3行(Context)

# 使用正则表达式
grep "^root" /etc/passwd           # 以 root 开头的行
grep "bash$" /etc/passwd           # 以 bash 结尾的行
grep -E "error|warning|critical" file  # 扩展正则(多个模式)
grep -P "\d{3}-\d{4}" file        # Perl 正则表达式

2.2 grep 进阶技巧

Bash
# 从标准输入搜索
ps aux | grep nginx                # 在进程列表中搜索
ps aux | grep "[n]ginx"            # 排除 grep 自身

# 多模式搜索
grep -e "error" -e "warning" file      # 多个 -e
grep -f patterns.txt file              # 从文件读取模式

# 统计和排序
grep -c "error" /var/log/*.log | sort -t: -k2 -rn   # 各文件错误数排序

# 只输出匹配部分
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' access.log

# 搜索二进制文件
grep -a "string" binary_file       # 将二进制作为文本处理

# 搜索压缩文件
zgrep "error" file.gz              # 搜索 gzip 压缩文件
bzgrep "error" file.bz2            # 搜索 bzip2 压缩文件

📖 3. sed — 流编辑器

3.1 基本用法

Bash
# 基本语法:sed [选项] '命令' 文件

# 替换(最常用的操作)
sed 's/old/new/' file              # 替换每行第一个匹配
sed 's/old/new/g' file             # 全局替换(每行所有匹配)
sed 's/old/new/2' file             # 替换每行第2个匹配
sed 's/old/new/gi' file            # 全局替换 + 忽略大小写

# 选项
sed -n 's/old/new/p' file         # 只打印替换了的行
sed -i 's/old/new/g' file         # 直接修改文件(就地编辑)
sed -i.bak 's/old/new/g' file     # 修改前备份为 file.bak
sed -e 's/a/b/' -e 's/c/d/' file  # 多个编辑命令

# 使用不同的分隔符(当替换内容含 / 时很有用)
sed 's|/usr/local|/opt|g' file    # 用 | 作分隔符
sed 's#http://#https://#g' file   # 用 # 作分隔符

3.2 地址与范围

Bash
# 行号地址
sed '3s/old/new/' file             # 只替换第3行
sed '1,5s/old/new/' file           # 替换第1到5行
sed '3,$s/old/new/' file           # 从第3行到最后一行
sed '1~2s/old/new/' file           # 从第1行开始每隔2行替换(奇数行)

# 正则地址
sed '/error/s/old/new/' file       # 包含 error 的行中替换
sed '/^#/d' file                   # 删除以 # 开头的行(注释)
sed '/start/,/end/s/a/b/' file     # 在 start 到 end 范围内替换

# 取反
sed '/^$/!s/^/  /' file            # 非空行添加缩进

3.3 常用操作

Bash
# 删除
sed 'd' file                      # 删除所有行
sed '3d' file                     # 删除第3行
sed '1,5d' file                   # 删除第1到5行
sed '/^$/d' file                  # 删除空行
sed '/^#/d' file                  # 删除注释行
sed '/^$/d;/^#/d' file            # 删除空行和注释行

# 插入和追加
sed '3i\新插入的行' file            # 在第3行前插入(insert)
sed '3a\新追加的行' file            # 在第3行后追加(append)
sed '1i\#!/bin/bash' script.sh    # 在文件开头插入

# 替换整行
sed '3c\完全替换的新内容' file      # 替换第3行

# 打印
sed -n '5p' file                  # 只打印第5行
sed -n '1,10p' file               # 打印第1到10行
sed -n '/error/p' file            # 打印匹配行(类似 grep)
sed -n '5,10p' file               # 类似 head+tail

# 转换
sed 'y/abc/ABC/' file             # 逐字符转换(a→A, b→B, c→C)

# 多行操作
sed 'N;s/\n/ /' file              # 将两行合并为一行

3.4 sed 高级技巧

Bash
# 反向引用
sed 's/\(.*\):\(.*\)/\2:\1/' file           # 交换:前后内容
sed -E 's/([0-9]+)-([0-9]+)/\2-\1/' file    # 交换数字

# 添加行号
sed = file | sed 'N;s/\n/\t/'

# 在匹配行后添加内容
sed '/pattern/a\添加的新行' file

# 读取和写入文件
sed '/MARKER/r input.txt' file     # 在 MARKER 后插入文件内容
sed -n '/error/w errors.txt' file  # 将匹配行写入文件

# 处理配置文件
# 修改配置项
sed -i 's/^port=.*/port=8080/' config.conf
sed -i 's/^#\(allow_remote\)/\1/' config.conf  # 取消注释

# Hold Space 用法(高级)
# sed 有两个缓冲区:Pattern Space(处理区)和 Hold Space(保留区)
# h: pattern → hold(覆盖)
# H: pattern → hold(追加)
# g: hold → pattern(覆盖)
# G: hold → pattern(追加)
# x: 交换两个缓冲区

# 反转文件行序(类似 tac)
sed -n '1!G;h;$p' file

# 删除连续重复行(类似 uniq)
sed '$!N;/^\(.*\)\n\1$/!P;D' file

📖 4. awk — 模式匹配与数据处理

4.1 基本概念

Bash
# awk 是一门完整的编程语言,擅长处理结构化文本数据
# 基本语法:awk 'pattern {action}' file
# awk 逐行读取输入,自动将每行分割成字段

# 字段变量
# $0  整行内容
# $1  第一个字段
# $2  第二个字段
# $NF 最后一个字段
# NR  当前行号(Number of Records)
# NF  当前行字段数(Number of Fields)
# FS  字段分隔符(Field Separator,默认空格/Tab)
# OFS 输出字段分隔符(Output Field Separator)
# RS  记录分隔符(Record Separator,默认换行)
# ORS 输出记录分隔符

# 示例数据:employees.txt
# Alice   Engineering  85000
# Bob     Marketing    72000
# Charlie Engineering  92000
# Diana   Marketing    68000
# Eve     Engineering  78000

4.2 基本用法

Bash
# 打印特定字段
awk '{print $1}' employees.txt             # 打印第一列(名字)
awk '{print $1, $3}' employees.txt         # 打印第1和第3列
awk '{print $NF}' employees.txt            # 打印最后一列
awk '{print NR, $0}' employees.txt         # 打印行号和整行

# 格式化输出
awk '{printf "%-10s %-15s %d\n", $1, $2, $3}' employees.txt

# 指定分隔符
awk -F: '{print $1, $7}' /etc/passwd       # 以 : 分隔
awk -F, '{print $1, $2}' data.csv          # 处理 CSV
awk 'BEGIN{FS=":"} {print $1}' /etc/passwd # BEGIN 块中设置

# 设置输出分隔符
awk -F: 'BEGIN{OFS=","} {print $1,$3,$7}' /etc/passwd

4.3 模式匹配

Bash
# 正则模式
awk '/Engineering/' employees.txt          # 包含 Engineering 的行
awk '!/Marketing/' employees.txt           # 不包含 Marketing 的行
awk '/^A/' employees.txt                   # A 开头的行

# 表达式模式
awk '$3 > 80000' employees.txt             # 薪资大于80000
awk '$3 > 80000 {print $1, $3}' employees.txt  # 筛选并打印
awk '$2 == "Engineering"' employees.txt    # 部门是 Engineering
awk 'NR >= 2 && NR <= 4' employees.txt     # 第2到4行

# 范围模式
awk '/Bob/,/Diana/' employees.txt          # 从 Bob 到 Diana 的行

# BEGIN 和 END
awk 'BEGIN{print "=== 员工报表 ==="} {print $0} END{print "=== 共 "NR" 人 ==="}' employees.txt

# 组合条件
awk '$2 == "Engineering" && $3 > 80000' employees.txt
awk '$3 > 90000 || $3 < 70000' employees.txt

4.4 编程功能

Bash
# 变量
awk '{sum += $3} END{print "总薪资:", sum}' employees.txt
awk '{sum += $3; count++} END{print "平均薪资:", sum/count}' employees.txt

# 条件语句
awk '{
    if ($3 > 80000)
        print $1, "高薪"
    else if ($3 > 70000)
        print $1, "中等"
    else
        print $1, "一般"
}' employees.txt

# 循环
awk '{for(i=1; i<=NF; i++) print NR, i, $i}' employees.txt

# 数组(关联数组)
# 统计每个部门的人数
awk '{dept[$2]++} END{for(d in dept) print d, dept[d]}' employees.txt

# 统计每个部门的总薪资
awk '{dept[$2] += $3} END{for(d in dept) printf "%-15s %d\n", d, dept[d]}' employees.txt

# 字符串函数
awk '{print length($1), $1}' employees.txt         # 字符串长度
awk '{print toupper($1)}' employees.txt             # 转大写
awk '{print tolower($2)}' employees.txt             # 转小写
awk '{print substr($1, 1, 3)}' employees.txt        # 子串
awk '{sub(/Engineering/, "Eng"); print}' employees.txt  # 替换
awk '{gsub(/a/, "A"); print}' employees.txt         # 全局替换
awk '{n = split($0, arr, " "); print n}' employees.txt # 分割

# 数学函数
awk 'BEGIN{print sin(1); print sqrt(144); print int(3.7)}'

4.5 awk 实战范例

Bash
# 处理 /etc/passwd
# 打印所有普通用户(UID>=1000)
awk -F: '$3 >= 1000 {print $1, $3, $7}' /etc/passwd

# 统计每种 Shell 的用户数
awk -F: '{shell[$NF]++} END{for(s in shell) print s, shell[s]}' /etc/passwd

# 处理 CSV 文件
# data.csv:
# Name,Age,City,Score
# Alice,25,Beijing,92
# Bob,30,Shanghai,88
# Charlie,28,Guangzhou,95

awk -F, 'NR>1 && $4>90 {print $1, $4}' data.csv   # 分数>90的

# 处理 Nginx access.log
# 统计访问量 Top 10 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10

# 统计 HTTP 状态码分布
awk '{status[$9]++} END{for(s in status) print s, status[s]}' access.log

# 统计每小时请求量
# -F'[/:]' 分割后:$1=IP等 $2=月 $3=年 $4=小时 $5=分钟
awk -F'[/:]' '{hour=$4; count[hour]++} END{for(h in count) print h, count[h]}' access.log

# 找出响应时间最长的请求
awk '{if($NF > max) {max=$NF; line=$0}} END{print max, line}' access.log

# 多文件处理
awk 'FNR==1{print "--- " FILENAME " ---"} {print}' file1 file2

# awk 脚本文件
# script.awk:
# BEGIN { FS=","; OFS="\t"; print "Name", "Score" }
# NR > 1 && $4 > 90 { print $1, $4 }
# END { print "---Done---" }
awk -f script.awk data.csv

📖 5. 辅助文本工具

5.1 sort — 排序

Bash
sort file.txt                      # 字母排序
sort -n file.txt                   # 数值排序
sort -r file.txt                   # 逆序
sort -k2 file.txt                  # 按第2列排序
sort -k3 -n file.txt               # 按第3列数值排序
sort -t: -k3 -n /etc/passwd        # 指定分隔符排序
sort -u file.txt                   # 排序并去重
sort -h file.txt                   # 人类可读数字排序(1K, 2M, 3G)
sort -R file.txt                   # 随机排序
sort -k2,2 -k3,3n file.txt        # 多键排序(先按第2列,再按第3列数值)
sort -s -k2 file.txt               # 稳定排序

5.2 uniq — 去重

Bash
# 注意:uniq 只能去除相邻的重复行,通常需要先 sort
sort file.txt | uniq               # 去除重复行
sort file.txt | uniq -c            # 显示重复次数
sort file.txt | uniq -d            # 只显示重复的行
sort file.txt | uniq -u            # 只显示不重复的行
sort file.txt | uniq -ci           # 忽略大小写计数

# 实用组合
# 统计词频
cat file | tr ' ' '\n' | sort | uniq -c | sort -rn | head -10

5.3 cut — 列提取

Bash
cut -d: -f1 /etc/passwd            # 以:分隔,取第1列
cut -d: -f1,3 /etc/passwd          # 取第1和第3列
cut -d: -f1-3 /etc/passwd          # 取第1到第3列
cut -c1-10 file.txt                # 取每行第1到10个字符
cut -d',' -f2 data.csv             # CSV 取第2列
cut --complement -d: -f2 file      # 取除了第2列以外的所有列

5.4 paste — 列合并

Bash
paste file1 file2                  # 横向合并(Tab 分隔)
paste -d, file1 file2             # 以逗号分隔合并
paste -s file.txt                  # 将列转为行

# 实用示例
# 将单列转为3列
cat list.txt | paste - - -         # 每3个一行

5.5 tr — 字符转换

Bash
# 字符替换
echo "hello" | tr 'a-z' 'A-Z'     # 转大写: HELLO
echo "HELLO" | tr 'A-Z' 'a-z'     # 转小写: hello
echo "hello" | tr 'el' 'ip'       # e→i, l→p: hippo

# 删除字符
echo "hello 123 world" | tr -d '0-9'           # 删除数字
echo "hello   world" | tr -d ' '               # 删除空格
cat file | tr -d '\r'                          # 删除 Windows 换行符(\r)

# 压缩重复字符
echo "aabbcc" | tr -s 'a-z'       # abc(每种字符只保留一个)
echo "hello   world" | tr -s ' '  # hello world(压缩空格)

# 字符集补集
echo "hello 123" | tr -cd '0-9\n'  # 只保留数字: 123

5.6 wc — 统计

Bash
wc file.txt                        # 行数 单词数 字节数
wc -l file.txt                     # 行数
wc -w file.txt                     # 单词数
wc -c file.txt                     # 字节数
wc -m file.txt                     # 字符数
wc -L file.txt                     # 最长行的长度

# 统计目录中文件数
ls -1 | wc -l

# 统计代码行数
find . -name "*.py" -exec cat {} + | wc -l

📖 6. 管道与重定向

6.1 重定向

Bash
# 标准文件描述符
# 0 = stdin(标准输入)
# 1 = stdout(标准输出)
# 2 = stderr(标准错误)

# 输出重定向
command > file          # 标准输出写入文件(覆盖)
command >> file         # 标准输出追加到文件
command 2> file         # 标准错误写入文件
command 2>> file        # 标准错误追加到文件
command > file 2>&1     # 标准输出和错误都写入文件
command &> file         # 同上(Bash 简写)
command > out.log 2> err.log  # 分别重定向

# 输入重定向
command < file          # 从文件读取输入
command << EOF          # Here Document
line1
line2
EOF

command <<< "string"    # Here String

# /dev/null — 黑洞设备
command > /dev/null              # 丢弃标准输出
command 2> /dev/null             # 丢弃标准错误
command > /dev/null 2>&1         # 丢弃所有输出

6.2 管道

Bash
# 管道将前一个命令的标准输出作为后一个命令的标准输入
command1 | command2 | command3

# 经典组合
cat /etc/passwd | grep "/bin/bash" | cut -d: -f1    # 找 bash 用户
ps aux | grep nginx | grep -v grep                   # 找 nginx 进程
du -sh /var/log/* | sort -rh | head -10              # 最大的日志文件
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

# tee — 同时输出到屏幕和文件
command | tee output.txt           # 看到输出,同时保存文件
command | tee -a output.txt        # 追加模式
command 2>&1 | tee log.txt         # 标准输出和错误都记录

# xargs — 将标准输入转为命令参数
find . -name "*.tmp" | xargs rm                     # 删除找到的文件
find . -name "*.py" | xargs grep "import"           # 搜索 Python 文件
echo "file1 file2 file3" | xargs -n1 echo           # 逐个执行
find . -name "*.jpg" | xargs -I{} cp {} /backup/    # 使用占位符

# 进程替换(Bash 特性)
diff <(ls dir1) <(ls dir2)       # 比较两个目录的文件列表
comm <(sort file1) <(sort file2) # 比较两个排序后的文件

📖 7. 实战案例

案例 1 : Nginx 日志分析

Bash
# 假设日志格式:
# 192.168.1.100 - - [15/Jan/2024:10:30:45 +0800] "GET /api/users HTTP/1.1" 200 1234

# 1. 统计总请求量
wc -l access.log

# 2. 统计访问量 Top 10 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10

# 3. 统计 HTTP 状态码分布
awk '{print $9}' access.log | sort | uniq -c | sort -rn

# 4. 统计最频繁的请求 URL
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -10

# 5. 统计每小时请求量
# -F'[:[]' 分割后:$1=IP等 $2=日期(15/Jan/2024) $3=小时
awk -F'[:[]' '{print $3}' access.log | sort | uniq -c

# 6. 查找 5xx 错误
awk '$9 ~ /^5/' access.log

# 7. 统计 404 请求
awk '$9 == "404" {print $7}' access.log | sort | uniq -c | sort -rn | head -10

# 8. 计算总带宽
awk '{sum += $10} END{printf "Total: %.2f MB\n", sum/1024/1024}' access.log

# 9. 找出慢请求(假设最后一列是响应时间)
awk '{if ($NF > 3) print}' access.log | head -20

# 10. 生成完整的日志分析报告
cat << 'SCRIPT' > analyze_log.sh
#!/bin/bash
LOG="${1:-access.log}"
echo "========== Nginx 日志分析报告 =========="
echo "文件: $LOG"
echo "总请求数: $(wc -l < "$LOG")"
echo ""
echo "--- Top 10 IP ---"
awk '{print $1}' "$LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- HTTP 状态码分布 ---"
awk '{print $9}' "$LOG" | sort | uniq -c | sort -rn
echo ""
echo "--- Top 10 请求URL ---"
awk '{print $7}' "$LOG" | sort | uniq -c | sort -rn | head -10
echo "========================================"
SCRIPT
chmod +x analyze_log.sh

案例 2 :系统日志分析

Bash
# 查找登录失败的记录
grep "Failed password" /var/log/auth.log | \  # grep文本搜索:按模式匹配行
  awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10  # $()命令替换:执行命令并获取输出

# 查找 SSH 暴力破解 IP
grep "Failed password" /var/log/auth.log | \
  awk '{for(i=1;i<=NF;i++) if($i=="from") print $(i+1)}' | \
  sort | uniq -c | sort -rn | head -20

# 统计今天不同级别的日志数量
grep "$(date +%b\ %d)" /var/log/syslog | \
  grep -oE '(error|warning|info|debug|critical)' -i | \
  tr 'A-Z' 'a-z' | sort | uniq -c | sort -rn

案例 3 :数据处理

Bash
# 处理 CSV 数据
# sales.csv:
# Date,Product,Quantity,Price
# 2024-01-01,Widget,100,9.99
# 2024-01-01,Gadget,50,19.99
# 2024-01-02,Widget,80,9.99

# 计算总销售额
awk -F, 'NR>1 {total += $3 * $4} END{printf "总销售额: $%.2f\n", total}' sales.csv

# 按产品统计销量
awk -F, 'NR>1 {qty[$2] += $3} END{for(p in qty) print p, qty[p]}' sales.csv  # Shell for循环

# 按日期统计收入
awk -F, 'NR>1 {rev[$1] += $3*$4} END{for(d in rev) printf "%s: $%.2f\n", d, rev[d]}' sales.csv  # awk文本处理:按列提取和格式化数据

# 提取特定信息并格式化
awk -F, 'NR>1 {printf "%-12s %-10s %5d  $%8.2f\n", $1, $2, $3, $3*$4}' sales.csv

# 文本替换批处理
# 批量替换多个文件中的字符串
find . -name "*.conf" -exec sed -i 's/old_server/new_server/g' {} +  # sed流编辑器:文本替换与转换

# 批量修改文件编码
find . -name "*.txt" -exec bash -c 'iconv -f GBK -t UTF-8 "$1" > "$1.utf8" && mv "$1.utf8" "$1"' _ {} \;  # &&前一个成功才执行后一个;||前一个失败才执行

📖 8. 面试要点

高频面试题

Q1 : grep 、 sed 、 awk 三者的区别和适用场景?

  • grep:文本搜索(过滤),从输入中找出匹配行
  • sed:流编辑,对文本进行替换、删除、插入等操作
  • awk:文本处理编程语言,擅长分列处理和统计计算 简记: grep 负责找, sed 负责改, awk 负责算。

Q2 :写一个命令统计 Nginx 日志中访问量 Top 10 的 IP

Bash
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10  # |管道:将前一命令的输出作为后一命令的输入

Q3 :如何用 sed 删除文件中的空行和注释行?

Bash
sed '/^$/d;/^#/d' file          # 删除空行和 # 注释行
sed '/^[[:space:]]*$/d;/^[[:space:]]*#/d' file  # 包括只有空格的"空行"

Q4 :解释 awk '{sum+=$NF} END{print sum}' 的含义

对每行取最后一个字段($NF)累加到变量 sum 中,处理完所有行后在 END 块中打印总和。

Q5 :>>> 的区别?2>&1 是什么意思?

> 覆盖写入,>> 追加写入。2>&1 将标准错误(文件描述符 2)重定向到标准输出(文件描述符 1)所指向的地方。


🔧 练习题

基础练习

  1. 使用 grep 从 /etc/passwd 中找出所有使用 bash 的用户
  2. 使用 sed 将文件中所有的 http:// 替换为 https://
  3. 使用 awk 打印 /etc/passwd 中 UID 大于 1000 的用户名和 UID

进阶练习

  1. 统计一个 Python 源码文件中 import 了哪些模块
  2. 从一段日志中提取所有 IP 地址并去重统计
  3. 编写 awk 脚本处理 CSV 文件,输出格式化报表

综合实战

  1. 编写 Nginx 日志分析脚本(统计 IP 、状态码、 URL 、带宽)
  2. 编写脚本分析 /var/log/auth.log 统计 SSH 登录失败次数

✅ 自我检查

  • 能写出常见的正则表达式( IP 、邮箱、日期)
  • 能用 grep 进行复杂的文本搜索
  • 能用 sed 完成替换、删除、插入操作
  • 能用 awk 进行分列处理和统计计算
  • 熟练使用 sort/uniq/cut/tr/wc
  • 理解管道和重定向的原理
  • 能独立完成日志分析任务

上一章02-文件与目录管理 下一章04-用户与权限管理