文本处理
Linux 系统提供了强大的文本处理工具链,可以高效地搜索、过滤、转换和分析文本数据。这些工具是系统管理、日志分析和数据处理的核心技能。
工具概览
Linux 文本处理工具可以分为几类:
| 工具 | 类型 | 核心功能 |
|---|---|---|
grep | 搜索过滤 | 按模式搜索文本行 |
sed | 流编辑器 | 文本替换、删除、插入 |
awk | 数据处理 | 字段处理、格式化输出、计算 |
sort | 排序 | 按各种条件排序文本行 |
uniq | 去重 | 去除重复行 |
cut | 列提取 | 按字段或字符提取内容 |
tr | 字符转换 | 字符替换、删除、压缩 |
wc | 统计 | 统计行数、字数、字节数 |
工具协作理念:Linux 的文本处理工具遵循"做好一件事"的 Unix 哲学。每个工具专注于单一功能,通过管道(|)组合使用,可以完成复杂的文本处理任务。
grep - 文本搜索
grep(Global Regular Expression Print)是最常用的文本搜索工具,用于在文件中查找匹配指定模式的行。
基本工作原理
grep 逐行读取输入,将每一行与给定的模式进行匹配,如果匹配成功则输出该行。它支持三种正则表达式语法:
- BRE(Basic Regular Expression):基本正则表达式,默认模式
- ERE(Extended Regular Expression):扩展正则表达式,使用
-E选项 - PCRE(Perl Compatible Regular Expression):Perl 兼容正则表达式,使用
-P选项
在 GNU grep 中,BRE 和 ERE 功能相同,只是语法略有差异。ERE 语法更简洁,推荐使用。
基本用法
# 在文件中搜索字符串
grep "error" /var/log/syslog
# 搜索多个文件
grep "error" file1.txt file2.txt
# 递归搜索目录
grep -r "function" ./src/
# 从标准输入搜索
cat file.txt | grep "pattern"
# 使用扩展正则表达式
grep -E "pattern1|pattern2" file.txt
解释:
- grep 默认输出匹配的整行内容
- 当搜索多个文件时,输出会包含文件名前缀
-r选项会递归搜索子目录中的所有文件
常用选项
匹配控制
# -i:忽略大小写
grep -i "ERROR" file.txt # 匹配 error, ERROR, Error 等
# -v:反向匹配,显示不匹配的行
grep -v "^#" config.conf # 排除注释行
# -w:匹配整个单词
grep -w "log" file.txt # 匹配 "log" 但不匹配 "logging" 或 "catalog"
# -x:匹配整行
grep -x "exact line" file.txt # 只匹配完全相同的整行
# -F:固定字符串模式(不解析正则表达式)
grep -F "a*b" file.txt # 字面匹配 "a*b",而不是正则表达式
# -e:指定多个模式
grep -e "error" -e "warning" -e "critical" file.txt
# -f:从文件读取模式
grep -f patterns.txt file.txt # patterns.txt 每行一个搜索模式
使用场景:
-i在搜索配置文件或日志时很有用,因为大小写可能不一致-v常用于过滤掉不需要的内容,如注释行或空白行-w可以避免部分匹配,比如搜索 "cat" 时不匹配 "category"-F在搜索包含正则元字符的字符串时使用,避免转义
输出控制
# -c:只输出匹配行数
grep -c "error" file.txt # 输出:15(匹配了 15 行)
# -l:只输出匹配的文件名
grep -l "TODO" *.py # 列出包含 TODO 的 Python 文件
# -L:只输出不匹配的文件名
grep -L "copyright" *.py # 列出不包含 copyright 的文件
# -n:显示行号
grep -n "error" file.txt # 输出:42:error message here
# -o:只输出匹配的部分
grep -o "[0-9]\+" file.txt # 只输出数字部分
# -q:静默模式(不输出,只返回状态码)
if grep -q "pattern" file.txt; then
echo "找到匹配"
fi
# -s:抑制错误信息
grep -s "pattern" nonexistent.txt # 不输出"文件不存在"的错误
解释:
-c常用于统计,如统计日志中的错误数量-l和-L适合批量处理文件时筛选-n帮助定位匹配行在文件中的位置-q主要用于脚本中的条件判断
上下文显示
# -A n:显示匹配行及其后 n 行
grep -A 3 "error" file.txt # 显示匹配行和后面 3 行
# -B n:显示匹配行及其前 n 行
grep -B 2 "error" file.txt # 显示匹配行和前面 2 行
# -C n:显示匹配行及其前后各 n 行
grep -C 5 "error" file.txt # 显示匹配行和前后各 5 行
# 组合使用
grep -B 2 -A 3 "error" file.txt # 前面 2 行,后面 3 行
使用场景:查看日志时,错误信息通常与上下文相关,使用上下文选项可以帮助理解错误的完整情况。
搜索范围控制
# -r:递归搜索目录
grep -r "pattern" /path/to/dir/
# -R:递归搜索,跟随符号链接
grep -R "pattern" /path/to/dir/
# --include:只搜索特定文件
grep -r --include="*.py" "import" ./src/
# --exclude:排除特定文件
grep -r --exclude="*.log" "error" /var/log/
# --exclude-dir:排除目录
grep -r --exclude-dir=".git" "TODO" ./
# --include-from:从文件读取包含规则
grep -r --include-from=include.txt "pattern" ./
正则表达式
正则表达式是描述文本模式的形式语言。grep 支持丰富的正则表达式语法。
基本元字符
# . 匹配任意单个字符(除换行符)
grep "a.c" file.txt # 匹配 abc, a1c, a@c 等
# * 匹配前一个字符 0 次或多次
grep "ab*c" file.txt # 匹配 ac, abc, abbc, abbbc 等
# + 匹配前一个字符 1 次或多次(需要 -E)
grep -E "ab+c" file.txt # 匹配 abc, abbc, abbbc 等(不匹配 ac)
# ? 匹配前一个字符 0 次或 1 次(需要 -E)
grep -E "colou?r" file.txt # 匹配 color 或 colour
# {n} 匹配前一个字符恰好 n 次(需要 -E)
grep -E "a{3}" file.txt # 匹配 aaa
# {n,m} 匹配前一个字符 n 到 m 次(需要 -E)
grep -E "a{2,4}" file.txt # 匹配 aa, aaa, aaaa
# {n,} 匹配前一个字符至少 n 次(需要 -E)
grep -E "a{2,}" file.txt # 匹配 aa, aaa, aaaa, ...
解释:
- 在基本正则表达式(BRE)中,
+、?、{}需要转义:\+、\?、\{n\} - 使用
-E选项启用扩展正则表达式,可以直接使用这些元字符 *在正则表达式中的含义与 shell 通配符不同,它表示"前一个字符的重复"而不是"任意字符"
字符类
# [...] 匹配方括号中的任意一个字符
grep "[aeiou]" file.txt # 匹配任意元音字母
grep "[0-9]" file.txt # 匹配任意数字
grep "[a-zA-Z]" file.txt # 匹配任意字母
# [^...] 匹配不在方括号中的任意字符
grep "[^0-9]" file.txt # 匹配任意非数字字符
# 预定义字符类
grep "[[:digit:]]" file.txt # 匹配数字 [0-9]
grep "[[:alpha:]]" file.txt # 匹配字母 [a-zA-Z]
grep "[[:alnum:]]" file.txt # 匹配字母和数字 [a-zA-Z0-9]
grep "[[:space:]]" file.txt # 匹配空白字符
grep "[[:upper:]]" file.txt # 匹配大写字母
grep "[[:lower:]]" file.txt # 匹配小写字母
grep "[[:punct:]]" file.txt # 匹配标点符号
解释:预定义字符类如 [[:digit:]] 比 [0-9] 更具可移植性,在不同字符编码的系统上表现一致。
位置锚点
# ^ 匹配行首
grep "^error" file.txt # 匹配以 error 开头的行
grep "^#" file.txt # 匹配以 # 开头的行(注释行)
# $ 匹配行尾
grep "error$" file.txt # 匹配以 error 结尾的行
grep "^$" file.txt # 匹配空行
# \< 匹配单词开头
grep "\<log" file.txt # 匹配 log, logging 但不匹配 catalog
# \> 匹配单词结尾
grep "log\>" file.txt # 匹配 log, catalog 但不匹配 logging
# \b 匹配单词边界
grep -E "\blog\b" file.txt # 精确匹配单词 log
解释:锚点本身不匹配任何字符,它们只匹配位置。这在精确匹配时非常有用。
分组和引用
# (...) 分组(需要 -E)
grep -E "(ab)+" file.txt # 匹配 ab, abab, ababab 等
# \1, \2, ... 引用前面的分组
grep -E "(.)(.)\2\1" file.txt # 匹配 abba, 1221, xyxy 等回文模式
# | 或运算(需要 -E)
grep -E "cat|dog" file.txt # 匹配 cat 或 dog
grep -E "(error|warning|critical)" file.txt
解释:分组将多个字符视为一个整体,可以进行重复或引用。反向引用可以匹配前面分组捕获的内容,常用于检测重复模式。
实用案例
日志分析
# 统计日志中的错误数量
grep -c "ERROR" /var/log/app.log
# 查找包含特定日期的错误
grep "2024-03-15.*ERROR" /var/log/app.log
# 查找特定 IP 的访问记录
grep "192.168.1.100" /var/log/nginx/access.log
# 提取日志中的所有 IP 地址
grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log | sort | uniq -c | sort -nr | head -10
# 查找错误并显示上下文
grep -C 10 "FATAL" /var/log/app.log
代码搜索
# 查找所有 TODO 注释
grep -rn "TODO" ./src/
# 查找函数定义
grep -rn "function\s\+\w\+" ./src/
# 查找未使用的变量(示例)
grep -rn "var\s\+\w\+\s*=" ./src/ | grep -v "used"
# 排除测试文件搜索
grep -r --exclude="*test*" "class User" ./src/
# 只搜索 Python 文件
grep -r --include="*.py" "import requests" ./
配置文件处理
# 排除注释和空行查看配置
grep -v "^#" /etc/nginx/nginx.conf | grep -v "^$"
# 查找配置项
grep "server_name" /etc/nginx/sites-enabled/*
# 查找被注释掉的配置
grep "^\s*#" /etc/ssh/sshd_config
sed - 流编辑器
sed(Stream Editor)是一个强大的流编辑器,用于对文本进行过滤和转换。它逐行处理输入,可以执行替换、删除、插入等操作。
工作原理
sed 的工作流程如下:
- 模式空间(Pattern Space):当前处理的行
- 保持空间(Hold Space):临时存储区域,用于高级操作
- 循环处理:每读取一行,执行所有命令,然后输出结果
基本语法
sed [选项] '命令' 文件
sed [选项] -f 脚本文件 文件
常用选项
# -n:静默模式,不自动输出
sed -n '5p' file.txt # 只输出第 5 行
# -e:指定多个命令
sed -e 's/foo/bar/' -e 's/baz/qux/' file.txt
# -f:从文件读取命令
sed -f script.sed file.txt
# -i:直接修改文件
sed -i 's/old/new/' file.txt
# -i.bak:修改前备份
sed -i.bak 's/old/new/' file.txt
# -E:使用扩展正则表达式
sed -E 's/[0-9]+/NUM/' file.txt
# -r:同 -E(兼容旧版本)
sed -r 's/[0-9]+/NUM/' file.txt
警告:-i 选项会直接修改原文件,建议先测试命令正确性,或使用 -i.bak 创建备份。
地址定位
sed 命令可以指定作用的行范围:
# 无地址:作用于所有行
sed 's/old/new/' file.txt
# 数字地址:指定行号
sed '5s/old/new/' file.txt # 只处理第 5 行
sed '5,10s/old/new/' file.txt # 处理第 5 到 10 行
sed '$s/old/new/' file.txt # 处理最后一行
# 正则表达式地址
sed '/error/s/old/new/' file.txt # 只处理包含 error 的行
sed '/^#/d' file.txt # 删除以 # 开头的行
# 范围地址
sed '/start/,/end/s/old/new/' file.txt # 从 start 行到 end 行之间
# 步长地址
sed '1~2s/old/new/' file.txt # 处理第 1, 3, 5, 7... 行(奇数行)
sed '2~2s/old/new/' file.txt # 处理第 2, 4, 6, 8... 行(偶数行)
# 反向匹配
sed '/pattern/!s/old/new/' file.txt # 不匹配 pattern 的行才替换
替换命令(s)
替换命令是 sed 最常用的功能:
# 基本替换
sed 's/old/new/' file.txt # 每行替换第一个匹配
# 全局替换
sed 's/old/new/g' file.txt # 替换所有匹配
# 替换第 n 个匹配
sed 's/old/new/2' file.txt # 只替换每行的第 2 个匹配
# 忽略大小写
sed 's/old/new/gi' file.txt # i 标志忽略大小写
# 只打印修改的行
sed -n 's/old/new/p' file.txt # 只输出发生替换的行
# 写入文件
sed 's/old/new/gw output.txt' file.txt # 将修改写入文件
# 使用不同的分隔符
sed 's|/usr/local|/usr|g' file.txt # 当模式包含 / 时,使用其他分隔符
sed 's#old#new#g' file.txt
替换中的特殊字符
# & 代表匹配到的整个字符串
sed 's/[0-9]\+/[&]/g' file.txt # 将数字用方括号括起来
# 输入:hello 123 world
# 输出:hello [123] world
# \1, \2, ... 代表分组捕获的内容
sed -E 's/([a-z]+) ([a-z]+)/\2 \1/' file.txt # 交换两个单词
# 输入:hello world
# 输出:world hello
# \u 转大写,\l 转小写
sed 's/\b[a-z]/\u&/g' file.txt # 首字母大写
# \U 到 \E 之间转大写
sed 's/\b[a-z]\+/\U&\E/g' file.txt # 单词全部大写
删除命令(d)
# 删除特定行
sed '5d' file.txt # 删除第 5 行
sed '5,10d' file.txt # 删除第 5 到 10 行
sed '$d' file.txt # 删除最后一行
# 删除匹配行
sed '/^$/d' file.txt # 删除空行
sed '/^#/d' file.txt # 删除注释行
sed '/DEBUG/d' file.txt # 删除包含 DEBUG 的行
# 删除不匹配的行
sed '/important/!d' file.txt # 只保留包含 important 的行
# 删除到文件末尾
sed '/pattern/,$d' file.txt # 删除从匹配行到末尾
插入和追加命令(i/a)
# i 在行前插入
sed '5i\new line' file.txt # 在第 5 行前插入
sed '/pattern/i\new line' file.txt # 在匹配行前插入
# a 在行后追加
sed '5a\new line' file.txt # 在第 5 行后追加
sed '/pattern/a\new line' file.txt # 在匹配行后追加
# 在文件开头添加
sed '1i\header line' file.txt
# 在文件末尾添加
sed '$a\footer line' file.txt
# 插入多行
sed '5i\
first line\
second line\
third line' file.txt
修改命令(c)
# 替换整行
sed '5c\replacement line' file.txt # 用新行替换第 5 行
# 替换匹配行
sed '/old/c\new content' file.txt # 用新内容替换包含 old 的行
# 替换范围行
sed '5,10c\replaced multiple lines' file.txt # 用一行替换 5-10 行
打印命令(p)
# 打印特定行
sed -n '5p' file.txt # 只打印第 5 行
sed -n '5,10p' file.txt # 打印第 5 到 10 行
# 打印匹配行
sed -n '/pattern/p' file.txt # 打印匹配的行(类似 grep)
# 打印行号
sed -n '/pattern/=' file.txt # 只打印匹配行的行号
# 打印行号和内容
sed -n '/pattern/{=;p}' file.txt
高级操作
多行处理
# N:读取下一行到模式空间
sed 'N;s/\n/ /' file.txt # 合并相邻两行
# D:删除模式空间的第一行
sed '/^$/{N;/^\n$/D}' file.txt # 删除多余空行,保留一个
# P:打印模式空间的第一行
sed -n 'N;P' file.txt # 打印奇数行
保持空间操作
# h:复制模式空间到保持空间
# H:追加模式空间到保持空间
# g:复制保持空间到模式空间
# G:追加保持空间到模式空间
# x:交换模式空间和保持空间
# 反转文件行顺序
sed '1!G;h;$!d' file.txt
# 交换相邻行
sed '{N;s/\(.*\)\n\(.*\)/\2\n\1/}' file.txt
# 合并奇偶行
sed 'N;s/\n/ /' file.txt
实用案例
配置文件修改
# 修改配置项
sed -i 's/DEBUG=false/DEBUG=true/' config.ini
# 注释掉某行
sed -i 's/^option=/#option=/' config.ini
# 取消注释
sed -i 's/^#\(option=\)/\1/' config.ini
# 添加配置项(如果不存在)
grep -q "new_option" config.ini || sed -i '$a\new_option=value' config.ini
# 修改 XML 配置
sed -i 's|<old>|<new>|g' config.xml
数据清洗
# 删除 Windows 换行符
sed -i 's/\r$//' file.txt
# 删除行首空格
sed 's/^[ \t]*//' file.txt
# 删除行尾空格
sed 's/[ \t]*$//' file.txt
# 同时删除首尾空格
sed 's/^[ \t]*//;s/[ \t]*$//' file.txt
# 压缩连续空格
sed 's/ \+/ /g' file.txt
# 删除空行
sed '/^$/d' file.txt
# 删除连续空行(保留一个)
sed '/^$/N;/^\n$/D' file.txt
批量文件处理
# 批量替换多个文件
sed -i 's/old/new/g' *.txt
# 批量替换并备份
sed -i.bak 's/old/new/g' *.txt
# 只修改匹配的文件
grep -rl "pattern" . | xargs sed -i 's/old/new/g'
# 在特定目录递归替换
find . -name "*.py" -exec sed -i 's/old/new/g' {} +
awk - 数据处理
awk 是一个强大的文本处理工具,特别适合处理结构化数据(如 CSV、日志文件等)。它将每行分割成字段,支持条件判断、循环、函数等编程特性。
基本概念
awk 的核心概念:
- 记录(Record):默认是一行文本
- 字段(Field):记录中的列,默认以空白字符分隔
- 模式(Pattern):匹配条件
- 动作(Action):匹配成功后执行的操作
基本语法:
awk '模式 { 动作 }' 文件
awk -F 分隔符 '模式 { 动作 }' 文件
字段操作
# $0 代表整行
awk '{ print $0 }' file.txt # 打印整行
# $1, $2, ... 代表字段
awk '{ print $1 }' file.txt # 打印第 1 列
awk '{ print $1, $3 }' file.txt # 打印第 1 和第 3 列
# $NF 代表最后一个字段
awk '{ print $NF }' file.txt # 打印最后一列
awk '{ print $(NF-1) }' file.txt # 打印倒数第二列
# NF 代表字段数量
awk '{ print NF }' file.txt # 打印每行的字段数
# NR 代表行号
awk '{ print NR, $0 }' file.txt # 打印行号和内容
# 指定分隔符
awk -F: '{ print $1 }' /etc/passwd # 以冒号分隔
awk -F',' '{ print $1 }' data.csv # 处理 CSV 文件
awk -F'\t' '{ print $1 }' data.tsv # 处理 TSV 文件
# 使用正则表达式作为分隔符
awk -F'[,:]' '{ print $1, $3 }' file.txt
解释:
- awk 自动将每行分割成字段,无需手动分割
- 字段索引从 1 开始,
$0表示整行 -F选项设置字段分隔符,默认是空白字符
模式匹配
# 正则匹配
awk '/pattern/ { print }' file.txt # 打印匹配的行
awk '/error/ { print $0 }' file.txt # 打印包含 error 的行
# 字段匹配
awk '$1 ~ /pattern/ { print }' file.txt # 第 1 列匹配
awk '$1 !~ /pattern/ { print }' file.txt # 第 1 列不匹配
# 比较运算
awk '$3 > 100 { print }' file.txt # 第 3 列大于 100
awk '$1 == "root" { print }' file.txt # 第 1 列等于 root
awk '$3 >= 80 && $3 <= 100 { print }' file.txt # 范围匹配
# 行号匹配
awk 'NR == 5 { print }' file.txt # 打印第 5 行
awk 'NR >= 5 && NR <= 10 { print }' file.txt # 打印 5-10 行
awk 'NR % 2 == 0 { print }' file.txt # 打印偶数行
# 范围模式
awk '/start/,/end/ { print }' file.txt # 从 start 到 end 之间的行
内置变量
# NR:当前记录号(行号)
awk '{ print NR, $0 }' file.txt
# FNR:当前文件的记录号(处理多文件时)
awk '{ print FNR, $0 }' file1.txt file2.txt
# NF:当前记录的字段数
awk '{ print NF }' file.txt
# FS:字段分隔符(输入)
awk 'BEGIN { FS=":" } { print $1 }' /etc/passwd
# OFS:字段分隔符(输出)
awk 'BEGIN { OFS="," } { print $1, $2 }' file.txt
# RS:记录分隔符(输入)
awk 'BEGIN { RS="" } { print }' file.txt # 以空行分隔记录
# ORS:记录分隔符(输出)
awk 'BEGIN { ORS="\n\n" } { print }' file.txt # 行间加空行
# FILENAME:当前文件名
awk '{ print FILENAME, NR, $0 }' file.txt
# ARGV:命令行参数数组
awk 'BEGIN { for (i=0; i<ARGC; i++) print ARGV[i] }' file1.txt file2.txt
BEGIN 和 END
# BEGIN:处理前执行
awk 'BEGIN { print "Header" } { print }' file.txt
# END:处理后执行
awk '{ print } END { print "Total lines:", NR }' file.txt
# 初始化变量
awk 'BEGIN { FS=":"; OFS="\t" } { print $1, $3 }' /etc/passwd
# 计算统计
awk '{ sum += $1 } END { print "Sum:", sum }' numbers.txt
# 计算平均值
awk '{ sum += $1; count++ } END { print "Average:", sum/count }' numbers.txt
条件语句
# if-else
awk '{ if ($3 > 90) print $1, "Excellent"; else print $1, "Good" }' grades.txt
# if-else if-else
awk '{
if ($3 >= 90) grade = "A"
else if ($3 >= 80) grade = "B"
else if ($3 >= 70) grade = "C"
else grade = "F"
print $1, grade
}' grades.txt
# 三元运算符
awk '{ print $1, ($3 >= 60 ? "Pass" : "Fail") }' grades.txt
循环语句
# for 循环
awk '{ for (i=1; i<=NF; i++) print $i }' file.txt # 打印每个字段
# while 循环
awk '{ i=1; while (i<=NF) { print $i; i++ } }' file.txt
# do-while 循环
awk '{ i=1; do { print $i; i++ } while (i<=NF) }' file.txt
# 遍历数组
awk '{
for (i=1; i<=NF; i++) {
count[$i]++
}
} END {
for (word in count) {
print word, count[word]
}
}' file.txt
数组
# 关联数组
awk '{ count[$1]++ } END { for (item in count) print item, count[item] }' file.txt
# 统计单词出现次数
awk '{
for (i=1; i<=NF; i++) {
words[$i]++
}
} END {
for (word in words) {
print word, words[word]
}
}' file.txt
# 数组排序
awk '{ arr[NR] = $0 } END {
n = asort(arr)
for (i=1; i<=n; i++) print arr[i]
}' file.txt
# 检查数组元素
awk '$1 in arr { print "found" }' file.txt
# 删除数组元素
awk '{ delete arr[$1] }' file.txt
内置函数
字符串函数
# length:字符串长度
awk '{ print length($0) }' file.txt
# substr:子字符串
awk '{ print substr($0, 1, 10) }' file.txt # 前 10 个字符
# index:查找子串位置
awk '{ print index($0, "pattern") }' file.txt
# split:分割字符串
awk '{ split($0, arr, ":"); print arr[1] }' file.txt
# sub:替换第一个匹配
awk '{ sub(/old/, "new"); print }' file.txt
# gsub:替换所有匹配
awk '{ gsub(/old/, "new"); print }' file.txt
# tolower/toupper:大小写转换
awk '{ print tolower($0) }' file.txt
awk '{ print toupper($0) }' file.txt
# match:正则匹配
awk '{ if (match($0, /[0-9]+/)) print substr($0, RSTART, RLENGTH) }' file.txt
数学函数
# 基本数学函数
awk '{ print int($1) }' file.txt # 取整
awk '{ print sqrt($1) }' file.txt # 平方根
awk '{ print log($1) }' file.txt # 自然对数
awk '{ print exp($1) }' file.txt # e 的幂次
awk '{ print sin($1) }' file.txt # 正弦
awk '{ print cos($1) }' file.txt # 余弦
# rand:随机数(0-1)
awk 'BEGIN { srand(); print rand() }'
# 生成指定范围随机整数
awk 'BEGIN { srand(); print int(rand() * 100) }' # 0-99
格式化输出
# printf 格式化
awk '{ printf "%-10s %5d %10.2f\n", $1, $2, $3 }' file.txt
# 格式说明符
# %s - 字符串
# %d - 整数
# %f - 浮点数
# %x - 十六进制
# %o - 八进制
# %- 左对齐
# %10 宽度 10
# %.2 小数点后 2 位
# 打印表格
awk 'BEGIN {
printf "%-15s %10s %10s\n", "Name", "Score", "Grade"
printf "%s\n", "------------------------------------"
}
{
printf "%-15s %10d %10s\n", $1, $2, $3
}' grades.txt
自定义函数
# 定义函数
awk '
function max(a, b) {
return (a > b) ? a : b
}
{
print max($1, $2)
}' file.txt
# 递归函数
awk '
function factorial(n) {
if (n <= 1) return 1
return n * factorial(n-1)
}
BEGIN {
print factorial(5) # 输出 120
}'
# 局部变量
awk '
function myfunc(x, y, z) { # y, z 是局部变量
y = x * 2
z = y + 1
return z
}
{
print myfunc($1)
}' file.txt
实用案例
日志分析
# 统计 IP 访问量
awk '{ ip[$1]++ } END { for (i in ip) print ip[i], i }' access.log | sort -rn | head -10
# 统计状态码分布
awk '{ status[$9]++ } END { for (s in status) print s, status[s] }' access.log
# 计算平均响应时间
awk '{ sum += $10; count++ } END { print "Avg:", sum/count, "ms" }' access.log
# 统计每小时的请求数
awk '{
split($4, time, ":")
hour = time[2]
count[hour]++
} END {
for (h in count) print h, count[h]
}' access.log | sort
# 提取慢请求(响应时间超过 1 秒)
awk '$10 > 1000 { print $0 }' access.log
数据统计
# 计算列的总和
awk '{ sum += $1 } END { print sum }' numbers.txt
# 计算平均值、最大值、最小值
awk '
BEGIN { max = -999999; min = 999999 }
{
sum += $1
count++
if ($1 > max) max = $1
if ($1 < min) min = $1
}
END {
print "Count:", count
print "Sum:", sum
print "Average:", sum/count
print "Max:", max
print "Min:", min
}' numbers.txt
# 按 key 分组求和
awk '{ sum[$1] += $2 } END { for (k in sum) print k, sum[k] }' data.txt
CSV 处理
# 提取特定列
awk -F',' '{ print $1, $3 }' data.csv
# 按条件过滤
awk -F',' '$3 > 100 { print }' data.csv
# 添加列标题
awk -F',' 'BEGIN { print "Name,Age,Score" } { print }' data.csv
# 转换格式
awk -F',' '{ printf "%-20s %5s %10s\n", $1, $2, $3 }' data.csv
# 合并多个 CSV 文件
awk -F',' 'FNR==1 && NR!=1 { next } { print }' *.csv > merged.csv
sort - 排序
sort 命令用于对文本行进行排序。
基本用法
# 基本排序(按字典序)
sort file.txt
# 反向排序
sort -r file.txt
# 数字排序
sort -n numbers.txt # 按数值排序
sort -rn numbers.txt # 按数值反向排序
# 按特定列排序
sort -k 2 data.txt # 按第 2 列排序
sort -k 2,2 -k 3,3 data.txt # 先按第 2 列,再按第 3 列
# 指定分隔符
sort -t: -k 3 -n /etc/passwd # 以冒号分隔,按第 3 列数字排序
# 去重排序
sort -u file.txt # 排序并去除重复行
# 检查是否已排序
sort -c file.txt # 如果未排序,输出第一个乱序的行
# 忽略前导空白
sort -b file.txt
# 忽略大小写
sort -f file.txt
# 月份排序
sort -M months.txt # JAN, FEB, MAR, ...
# 随机排序
sort -R file.txt
常用组合
# 按第 2 列数字降序排列
sort -k 2 -nr data.txt
# 排序后取前 10 名
sort -k 2 -nr data.txt | head -10
# 处理大文件(使用临时文件)
sort -S 1G -T /tmp bigfile.txt
# 合并已排序的文件
sort -m sorted1.txt sorted2.txt sorted3.txt
uniq - 去重
uniq 命令用于去除相邻的重复行。通常与 sort 配合使用。
基本用法
# 去除相邻重复行
uniq file.txt
# 统计重复次数
uniq -c file.txt
# 只显示重复行
uniq -d file.txt
# 只显示唯一行(不重复的)
uniq -u file.txt
# 忽略前 N 个字符
uniq -s 5 file.txt
# 只比较前 N 个字符
uniq -w 10 file.txt
# 忽略大小写
uniq -i file.txt
常用组合
# 统计每个值的出现次数并排序
sort file.txt | uniq -c | sort -rn
# 找出重复的行
sort file.txt | uniq -d
# 找出只出现一次的行
sort file.txt | uniq -u
# 找出出现次数最多的前 10 个
sort file.txt | uniq -c | sort -rn | head -10
cut - 列提取
cut 命令用于按列提取文本内容。
基本用法
# 按字符位置提取
cut -c 1-10 file.txt # 提取前 10 个字符
cut -c 5- file.txt # 从第 5 个字符到末尾
cut -c 1,5,10 file.txt # 提取第 1、5、10 个字符
# 按字段提取
cut -d: -f1 /etc/passwd # 以冒号分隔,提取第 1 字段
cut -d',' -f1,3 data.csv # 提取第 1 和第 3 字段
cut -d',' -f1-3 data.csv # 提取第 1 到第 3 字段
# 排除字段
cut -d',' --complement -f2 data.csv # 提取除第 2 字段外的所有字段
# 指定输出分隔符
cut -d: -f1,3 --output-delimiter=' ' /etc/passwd
tr - 字符转换
tr 命令用于字符转换、删除和压缩。
基本用法
# 字符替换
tr 'a-z' 'A-Z' < file.txt # 小写转大写
tr 'A-Z' 'a-z' < file.txt # 大写转小写
# 删除字符
tr -d '0-9' < file.txt # 删除所有数字
tr -d '\n' < file.txt # 删除换行符
tr -d '\r' < file.txt # 删除回车符
# 压缩连续字符
tr -s ' ' < file.txt # 压缩连续空格为一个
tr -s '\n' < file.txt # 压缩连续空行为一个
# 替换换行符
tr '\n' ' ' < file.txt # 换行转空格
tr ' ' '\n' < file.txt # 空格转换行
# 字符集
tr '[:lower:]' '[:upper:]' < file.txt # 小写转大写
tr -d '[:digit:]' < file.txt # 删除数字
tr -d '[:punct:]' < file.txt # 删除标点符号
wc - 统计
wc 命令用于统计行数、单词数和字节数。
基本用法
# 统计行数、单词数、字节数
wc file.txt # 输出:行数 单词数 字节数 文件名
# 只统计行数
wc -l file.txt
# 只统计单词数
wc -w file.txt
# 只统计字节数
wc -c file.txt
# 只统计字符数
wc -m file.txt
# 统计多个文件
wc *.txt
# 配合管道使用
cat file.txt | wc -l # 统计行数
grep "error" file.txt | wc -l # 统计匹配行数
组合应用
日志分析管道
# 统计访问量前 10 的 IP
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -10
# 统计状态码分布
awk '{ print $9 }' access.log | sort | uniq -c | sort -rn
# 找出响应时间最慢的 10 个请求
awk '{ print $10, $0 }' access.log | sort -rn | head -10
# 统计每分钟的请求数
awk '{ print substr($4, 14, 16) }' access.log | sort | uniq -c
# 找出访问量异常的 IP(超过 1000 次)
awk '{ ip[$1]++ } END { for (i in ip) if (ip[i] > 1000) print ip[i], i }' access.log
数据处理管道
# CSV 数据处理:提取、过滤、排序
awk -F',' '$3 > 100 { print $1, $2, $3 }' data.csv | sort -k 3 -nr
# 提取所有邮箱地址
grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' file.txt | sort -u
# 统计代码行数(排除空行和注释)
grep -v '^\s*$' code.py | grep -v '^\s*#' | wc -l
# 合并两个文件的特定列
paste <(cut -d',' -f1 file1.csv) <(cut -d',' -f2 file2.csv)
文本清洗管道
# 清洗数据:删除空白、去重、排序
cat data.txt | tr -s ' ' | sed 's/^[ \t]*//' | sort -u > clean.txt
# 格式化 JSON(需要 jq)
cat data.json | jq .
# 提取并格式化日期
grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' file.txt | sort | uniq -c
最佳实践
性能优化
-
处理大文件时:
- 使用
LC_ALL=C加速排序:LC_ALL=C sort bigfile.txt sort使用-S指定内存:sort -S 1G bigfile.txtawk比sed在处理复杂逻辑时更高效
- 使用
-
减少管道层级:
# 不推荐
cat file.txt | grep "pattern" | wc -l
# 推荐
grep -c "pattern" file.txt -
使用内置功能:
# 不推荐
grep "pattern" file.txt | awk '{ print $1 }'
# 推荐
awk '/pattern/ { print $1 }' file.txt
安全注意
-
特殊字符处理:
# 使用引号保护变量
grep "$pattern" file.txt
# 处理包含特殊字符的文件名
find . -name "*.txt" -print0 | xargs -0 grep "pattern" -
避免命令注入:
# 不要直接在 awk 中执行用户输入
# 危险:awk "{ system($1) }" file.txt -
备份重要文件:
# 使用 -i.bak 创建备份
sed -i.bak 's/old/new/g' important.conf
小结
本章我们学习了 Linux 文本处理的核心工具:
- grep:强大的文本搜索工具,支持正则表达式
- sed:流编辑器,用于文本替换和转换
- awk:数据处理工具,支持复杂的数据分析和格式化
- sort/uniq:排序和去重
- cut/tr/wc:列提取、字符转换和统计
这些工具通过管道组合,可以构建强大的文本处理流水线,是系统管理和数据分析的必备技能。
练习
- 从日志文件中提取所有错误信息,统计每种错误的出现次数
- 使用 sed 批量修改配置文件中的 IP 地址
- 使用 awk 分析 CSV 文件,计算某列的总和和平均值
- 编写一个管道命令,找出系统中占用内存最多的 10 个进程
- 使用文本处理工具分析 Web 服务器访问日志,生成访问报告