跳转至

文本处理三剑客

文本处理三剑客

📌 学习时间: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-用户与权限管理