Linux命令行与脚本-04-文本处理利器:sed、awk与正则表达式

全文摘要

本文将带你深入理解Linux下的文本处理工具,帮助你掌握sed、awk和正则表达式的强大功能。你将学到正则表达式的语法规则、sed流编辑器的使用技巧、awk编程语言的核心概念、以及如何组合使用这些工具处理复杂的文本数据。通过阅读本文,你将拥有处理日志文件、批量修改配置文件、提取分析数据的强大能力。

全书总结

文本处理是Linux系统管理的必备技能,sed、awk和grep是文本处理的”三剑客”。本文系统梳理了正则表达式的基础语法与高级模式、sed的查找替换与文本转换、awk的模式匹配与数据处理、以及grep/egrep/fgrep的使用差异。从简单的文本替换到复杂的数据提取,涵盖了文本处理的完整技能树。适合系统运维、数据处理工程师、日志分析人员、以及对文本自动化处理感兴趣的技术人员阅读。


一、正则表达式:模式匹配的语言

正则表达式是描述文本模式的强大语言,是sed、awk、grep等工具的基础。

flowchart TB
    subgraph RE[正则表达式基础]
        direction TB
        Literal[字面字符<br/>abc 匹配 "abc"]
        Dot[点号 .<br/>匹配任意单个字符]
        CharSet[字符集 [abc]<br/>匹配 a/b/c]
        Negate[^0-9]<br/>[br/>非数字]
        Anchor[锚点<br/>^行首 $行尾]
    end

    subgraph Quantifiers[量词]
        direction TB
        Star[*<br/>0次或多次]
        Plus[+<br/>1次或多次]
        Quest[?<br/>0次或1次]
        Range[{n,m}<br/>n到m次]
    end

    subgraph Advanced[高级模式]
        direction TB
        Group[捕获组 (...)<br/>保存匹配]
        Ref[反向引用 \n<br/>引用第n组]
        Bound[单词边界 \b<br/>单词边界]
        Back[后退查找 (?<=...)<br/>向前/向后查找]
    end

    RE --> Practice[实战应用]
    Quantifiers --> Practice
    Advanced --> Practice

    style Dot fill:#e3f2fd
    style Star fill:#fff9c4
    style Group fill:#c8e6c9

图表讲解:这张图展示了正则表达式的核心概念——正则表达式是文本处理的瑞士军刀。

基础元字符

  • .:匹配任意单个字符(除换行符外)
  • [abc]:字符集,匹配a、b或c中的任意一个
  • [^0-9]:否定字符集,匹配非数字的字符
  • ^:匹配行首
  • $:匹配行尾
  • \d:匹配数字(等同于[0-9]
  • \w:匹配单词字符(字母、数字、下划线)
  • \s:匹配空白字符(空格、制表符、换行符)

量词

  • *:匹配前导模式0次或多次
  • +:匹配前导模式1次或多次
  • ?:匹配前导模式0次或1次
  • {n}:匹配恰好n次
  • {n,m}:匹配n到m次

示例

  • ^[A-Z]:以大写字母开头的行
  • .*\.log$:以.log结尾的文件名
  • \berror\b:单词”error”(前后是单词边界,不会匹配”errornous”中的error)
  • 192\.168\.[0-9]+:匹配IP地址(如192.168.1.1)

二、sed:流编辑器

sed(Stream Editor)是强大的文本处理工具,用于查找和替换文本。

flowchart TB
    subgraph SedOps[sed操作模式]
        direction TB
        Sub[替换<br/>s/old/new/]
        Del[删除<br/>2d 删除第2行]
        Print[打印<br/>/pattern/p 打印匹配行]
        App[追加<br/>a\文本]
        Ins[插入<br/>i\文本]
    end

    subgraph SedInPlace[原地修改]
        direction TB
        Back[备份<br/>-i.bak]
        Direct[直接修改<br/>-i]
    end

    subgraph SedAdv[sed高级用法]
        direction TB
        Addr[地址范围<br/>1,10p 1到10行]
        Cmd[多命令<br/>-e 'cmd1' -e 'cmd2']
        Read[读取文件<br/>r file]
        Write[写入文件<br/>w file]
    end

    SedOps --> Tool[文本处理]
    SedInPlace --> Tool
    SedAdv --> Tool

    style Sub fill:#e3f2fd
    style Del fill:#ffcdd2
    style Print fill:#c8e6c9
    style Read fill:#ba68c8

图表讲解:这张图展示了sed的核心操作和高级功能——sed是批量文本修改的神器。

基本替换

# s/old/new/ 替换第一个匹配
sed 's/foo/bar/' file.txt
 
# s/old/new/g 全局替换
sed 's/foo/bar/g' file.txt
 
# 删除行
sed '/pattern/d' file.txt  # 删除匹配的行
sed '5d' file.txt         # 删除第5行
sed '/foo/,/bar/d' file.txt  # 删除foo和bar之间的行

常用选项

  • -n:默认打印所有行,-n取消自动打印
  • -i:原地修改文件(小心使用!)
  • -i.bak:修改前备份为file.bak
  • -e:指定多个命令

组合使用

# 删除空行
sed '/^$/d' file.txt
 
# 删除注释行
sed '/^#/d' file.txt
 
# 替换并删除空行
sed -e 's/foo/bar/' -e '/^$/d' file.txt

高级用法

# 地址范围:只处理5到10行
sed '5,10s/foo/bar/' file.txt
 
# 读入文件内容
sed '/pattern/r insert.txt' file.txt
 
# 写入到文件
sed 'w output.txt' file.txt

三、awk:模式扫描与数据处理语言

awk不仅是文本处理工具,更是一门完整的编程语言。

flowchart TB
    subgraph AwkFlow[awk执行流程]
        direction TB
        Begin[BEGIN块<br/>初始化]
        Input[逐行读取<br/>处理每一行]
        Action[模式匹配<br/>执行动作]
        End[END块<br/>清理工作]
    end

    subgraph AwkFeat[awk核心特性]
        direction TB
        FS[字段分隔符<br/>-F:指定分隔符]
        NF[字段数量<br/>$0整行 $NF最后字段]
        NR[行号<br/>FNR当前文件行号]
        Print[格式化输出<br/>printf]
    end

    subgraph AwkAdv[awk高级特性]
        direction TB
        Arr[数组<br/>关联数组]
        Func[函数<br/>自定义函数]
        Sys[系统调用<br/>getline system]
    end

    AwkFlow --> Use[数据处理]
    AwkFeat --> Use
    AwkAdv --> Use

    style Begin fill:#e3f2fd
    Action fill:#fff9c4
    End fill:#c8e6c9

图表讲解:这张图展示了awk的执行模型和核心特性——awk是数据提取和分析的利器。

基本语法

# 打印每一行
awk '{print $0}' file.txt
 
# 打印第1和第3字段
awk '{print $1, $3}' file.txt
 
# 模式匹配后打印
awk '/error/ {print $0}' file.txt

BEGIN和END

# BEGIN在处理前执行,END在处理后执行
awk 'BEGIN {print "Start"} {print $0} END {print "End"}' file.txt

常用内置变量

  • NR:当前行号
  • NF:字段数量
  • $0:整行
  • $1, $2, ..., $NF:第1、2…、最后一个字段
  • FS:输入字段分隔符(默认是空格)
  • OFS:输出字段分隔符(默认是空格)

实用例子

# 统计文件行数
awk 'END {print NR}' file.txt
 
# 打印字段数大于3的行
awk 'NF > 3 {print $0}' file.txt
 
# 计算数字列的和
awk '{sum += $1} END {print sum}' numbers.txt
 
# 提取特定字段的行
awk -F: '$1 == "root" {print $0}' /etc/passwd
 
# 格式化输出
awk '{printf "%-10s %5d\n", $1, $2}' file.txt

四、grep:文本搜索工具

grep是查找文本模式的利器,与sed、awk并称为”三剑客”。

flowchart TB
    subgraph GrepVars[grep变体]
        direction TB
        Grep[grep<br/>基础grep]
        Egrep[egrep<br/>扩展grep<br/>等同于grep -E]
        Fgrep[fgrep<br/>固定字符串<br/>不解释正则]
        Zgrep[zgrep<br/>压缩文件搜索]
    end

    subgraph GrepOpts[grep常用选项]
        direction TB
        I[i<br/>忽略大小写]
        V[v<br/>反向查找]
        Invert[n/v<br/>显示不匹配的行]
        Count[c<br/>只计数]
        Num[n<br/>显示行号]
        Cntxt[C n<br/>显示上下n行]
        Color[color<br/>高亮匹配]
        Recur[R<br/>递归搜索目录]
    end

    subgraph GrepOut[grep输出格式]
        CountOut[c<br/>只输出匹配数量]
        Line[n<br/>显示行号]
        Name[H<br/>只显示文件名]
        Match[o<br/>只输出匹配部分]
    end

    GrepVars --> GrepOpts
    GrepOpts --> GrepOut

    style Grep fill:#e3f2fd
    style I fill:#c8e6c9
    style V fill:#ba68c8

图表讲解:这张图展示了grep的变体和常用选项——grep是日常查找文本的首选工具。

grep基础

# 搜索包含"error"的行
grep "error" file.txt
 
# 忽略大小写搜索
grep -i "ERROR" file.txt
 
# 反向查找(不匹配的行)
grep -v "comment" file.txt
 
# 递归搜索目录
grep -r "TODO" /path/to/dir

常用组合

# 搜索并显示行号
grep -n "pattern" file.txt
 
# 显示匹配行及上下2行
grep -C 2 "error" file.txt
 
# 只显示文件名(不显示匹配行)
grep -l "pattern" /path/to/dir/*
 
# 统计匹配次数
grep -c "error" file.txt
 
# 只输出匹配部分(不显示整行)
grep -o "[0-9]\+" file.txt  # 提取所有数字

egrep和fgrep

  • egrep等同于grep -E,使用扩展正则表达式(如+?|不需要转义)。
  • fgrep等同于grep -F,固定字符串搜索(不解释正则,速度更快)。

管道组合

# 查找进程
ps aux | grep nginx
 
# 查找文件并显示行号
grep -n "pattern" file.txt | less
 
# 查找并计数
ps aux | grep -c python
 
# 组合多个grep
grep "ERROR" log.txt | grep -v "DEBUG"

五、文本处理实战案例

结合sed、awk、grep解决实际问题。

flowchart TB
    subgraph Tasks[常见文本处理任务]
        direction TB
        T1[批量替换<br/>配置文件修改]
        T2[日志分析<br/>提取错误信息]
        T3[数据提取<br/>CSV/JSON处理]
        T4[文件合并<br/>多文件处理]
        T5[格式转换<br/>日志格式标准化]
    end

    subgraph Tools[工具选择]
        direction TB
        Replace[任务1<br/>sed最合适]
        Search[任务2<br/>grep+awk]
        Extract[任务3<br/>awk最适合]
        Merge[任务4<br/>cat+awk]
        Convert[任务5<br/>sed+awk]
    end

    Tasks --> Tools --> Result[高效处理]

    style Replace fill:#e3f2fd
    style Search fill:#fff9c4
    style Extract fill:#c8e6c9

图表讲解:这张图展示了文本处理任务和工具选择——选择合适的工具是高效处理的关键。

案例1:批量修改配置文件

# 将所有配置文件的旧域名替换为新域名
sed -i 's/old.domain.com/new.domain.com/g' *.conf
 
# 注释掉包含"DEBUG"的行
sed -i 's/^DEBUG/#DEBUG/' app.conf

案例2:分析Web服务器日志

# 统计访问IP的数量
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
 
# 提取404错误的请求
awk '$9 == 404 {print $7}' access.log > 404_urls.txt
 
# 计算平均响应时间
awk '{sum+=$10; count++} END {print sum/count}' access.log

案例3:CSV数据处理

# 提取特定列
awk -F, '{print $1, $3}' data.csv > output.txt
 
# 计算某列的平均值
awk -F, 'NR>1 {sum+=$2; count++} END {print sum/count}' data.csv

案例4:查找特定模式的日志

# 查找今天的错误日志
grep "$(date +%Y-%m-%d).*ERROR" /var/log/app.log
 
# 查找特定用户的操作日志
grep "user=admin" /var/log/auth.log
 
# 查找包含IP地址的行
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file.txt

六、工具组合与脚本集成

将sed、awk、grep组合成强大的文本处理管道。

flowchart LR
    Input[输入数据<br/>文件/管道] --> Grep[grep<br/>过滤数据]
    Grep --> Sed[sed<br/>清洗数据]
    Sed --> Awk[awk<br/>提取计算]
    Awk --> Output[输出结果<br/>文件/屏幕]

    Input -.->|直接处理| Awk

    style Input fill:#e3f2fd
    style Grep fill:#fff9c4
    style Sed fill:#c8e6c9
    style Awk fill:#ba68c8
    style Output fill:#a5d6a7

图表讲解:这张图展示了文本处理管道的典型流程——工具组合产生1+1>2的效果。

管道组合示例

# 查找进程、提取PID、杀死进程
ps aux | grep nginx | awk '{print $2}' | xargs kill -9
 
# 分析日志:提取错误行、提取IP、统计访问次数
grep ERROR /var/log/app.log | awk '{print $5}' | sort | uniq -c | sort -rn
 
# 处理CSV:跳过标题行、提取字段、计算平均值
tail -n +2 data.csv | awk -F, '{sum+=$2; count++} END {print sum/count}'
 
# 多文件处理:查找所有.log文件、提取特定行、合并结果
cat *.log | grep "ERROR" | awk '{print $1,$2}' > errors.txt

脚本集成

#!/bin/bash
# 日志分析脚本
 
LOGFILE="/var/log/app.log"
DATE=$(date +%Y-%m-%d)
OUTPUT="report_$DATE.txt"
 
echo "Error Report - $DATE" > $OUTPUT
echo "=====================" >> $OUTPUT
echo "" >> $OUTPUT
 
# 提取今天的错误日志
grep "$(date +%Y-%m-%d).*ERROR" $LOGFILE | while read line; do
    # 提取时间戳、错误级别、错误消息
    timestamp=$(echo "$line" | awk '{print $1,$2}')
    message=$(echo "$line" | cut -d']' -f3-)
    echo "$timestamp $message" >> $OUTPUT
done
 
echo "Total errors: $(grep "$(date +%Y-%m-%d).*ERROR" $LOGFILE | wc -l)" >> $OUTPUT

结语

sed、awk、grep是Linux文本处理的”三剑客”,掌握它们能让你的文本处理效率提升数量级。

工具选择指南

  • grep:查找包含特定模式的行,适合过滤日志、搜索代码
  • sed:查找和替换文本,适合批量修改配置文件、删除/添加行
  • awk:结构化数据处理,适合提取字段、计算统计、格式化输出

学习路径

  1. 先掌握正则表达式基础,它是所有工具的基础
  2. 学习grep的基本用法,这是最常用的
  3. 学习sed的替换功能,解决常见的文本修改需求
  4. 最后学习awk,它是一门完整的编程语言

实践建议

  1. 在测试文件上验证命令,再用于重要文件
  2. 使用-i前先检查备份
  3. 复杂的脚本分段调试,逐步构建
  4. 善用管道,让工具各司其职

掌握文本处理工具后,你会发现很多之前需要手工操作的任务,一条命令就能搞定。这就是Linux命令行的威力。


常见问题解答

Q1:sed和awk有什么区别,什么时候用哪个?

:sed是流编辑器,适合简单的查找替换、删除、插入等操作。awk是编程语言,适合字段提取、计算、格式化输出等复杂任务。

经验法则:如果你只需要”修改”文本(替换、删除、添加行),用sed;如果你需要”分析”文本(提取字段、计算统计、格式化输出),用awk。

例如:sed 's/old/new/g' file(替换)vs awk '{sum+=$1} END {print sum}' file(计算)。两者可以组合使用:先grep过滤,再sed替换,最后awk分析。在管道中,它们各司其职,配合完成复杂的文本处理任务。


Q2:正则表达式中的.*.*?有什么区别?

.*是贪婪模式,匹配尽可能多的字符;.*?是非贪婪模式,匹配尽可能少的字符。

"foo.*bar"中,.匹配任意字符,*是量词(0次或多次)。对于文本”fooXXXbarYYYbar”,foo.*bar匹配”fooXXXbarYYYbar”(到最后的bar),foo.*?bar匹配”fooXXXbar”(到第一个bar)。

贪婪模式可能”匹配过多”,导致结果不符合预期。非贪婪模式更适合提取特定模式之间的内容。例如,提取HTML标签内容时,<p>.*?</p>匹配单个段落(而不是到

末尾)。


Q3:如何在awk中处理多个分隔符?

:awk默认使用单个字符作为字段分隔符(FS)。如果字段分隔符可能包含多个字符(如::::),或需要动态分隔符,可以用几种方法:(1)-F'::'指定多个字符为分隔符:awk -F'::' '{print $1, $3}'(冒号分隔)。注意-F指定的分隔符被当作字符串,需要引号。

(2)使用split()函数在行内动态分割:awk '{split($0, a, ":"); print a[1], a[3]}'。(3)用正则表达式作为字段分隔符:awk -F'[: ]+' '{print $1, $3}'(冒号和空格都是分隔符)。

对于CSV等格式,字段内包含分隔符的情况,需要更复杂的处理(如去除引号后分割)。


Q4:如何在awk中处理多个文件?

:awk可以处理多个文件,文件名存储在FILENAME变量中,行号在每个文件内从1开始(FNR),全局行号持续增加(NR):

 

这会在每行前加上文件名和行号。

可以在BEGIN块中初始化,在END块中汇总:

awk '{count[FILENAME]++} END {for (f in count) print f, count[f]}' *.txt

这会统计每个文件的行数。FNR==1 {print "Processing: " FILENAME}可以检测文件边界,在新文件开始时执行操作。


Q5:如何提高大文件的处理速度?

:大文件处理需要考虑性能优化:

  • 减少awk的字段分割awk '{print $1}'awk '{print $1, $3}'快(后者需要分割更多字段)。
  • 提前过滤:用grep过滤掉不需要的行,再交给awk处理:grep pattern file | awk '{...}'
  • 使用更快的工具:对于简单计数,grep -cawk '{count++} END {print count}'快。对于查找,grep -f(固定字符串)比正则表达式快。
  • 减少内存使用:不使用数组存储大量数据(除非必要),边读边处理。
  • 使用LC_ALL=C:设置locale为C,加速排序和字符串比较:LC_ALL=C sort file
  • 分块处理:对于超大文件,可以分割处理:split -l 1000000 large_file.txt分成小文件,逐个处理,最后合并结果。
  • 使用mawk:mawk是awk的现代实现,性能更好,支持更大的文件和更多特性。

更新时间:2026年3月2日 作者:Linux技术专栏 标签:#sed awk grep 正则表达式 文本处理