Shell 脚本中的文本处理:从基础到高级的实战技巧 文本处理是 Shell 脚本最擅长的领域之一。从简单的日志分析到复杂的数据转换,掌握文本处理技巧能让你轻松应对各种场景。本文将系统介绍 Shell 环境中常用的文本处理工具和技术,从基础的字符串操作到高级的正则表达式应用,帮助你成为 Shell 文本处理高手。 一、字符串基础操作:变量与字符串处理 在 Shell 中,字符串是最基本的数据类型,掌握其操作是文本处理的基础。
- 字符串变量的创建与拼接 bash #!/bin/bash
字符串创建
name="John" message='Hello, World' # 单引号中变量不会被解析
字符串拼接
greeting="Hello, $name!" # 双引号中可以解析变量 echo $greeting # 输出: Hello, John!
复杂拼接
full_name="John Doe"
welcome_message="Welcome, ${full_name}! Today is welcome_message" # 输出包含姓名和当前星期几
2. 字符串长度与截取
bash
#!/bin/bash
text="Shell Scripting"
获取长度
echo "长度: ${#text}" # 输出: 15
字符串截取
echo "前5个字符: ${text:0:5}" # 输出: Shell echo "从第6个字符开始: ${text:6}" # 输出: Scripting echo "从末尾取6个字符: ${text: -6}" # 输出: cripting (注意冒号后有空格)
按模式截取
filename="document.txt.bak"
移除最后一个 . 及其后面的内容
echo "文件名: ${filename%.*}" # 输出: document.txt
移除第一个 . 及其前面的内容
echo "扩展名: ${filename#*.}" # 输出: txt.bak
移除最后一个 . 及其前面的内容
echo "最终扩展名: ${filename##*.}" # 输出: bak 3. 字符串替换 bash #!/bin/bash
message="The quick brown fox jumps over the lazy dog."
替换第一个匹配项
echo "替换第一个: ${message/fox/cat}"
输出: The quick brown cat jumps over the lazy dog.
替换所有匹配项
echo "替换所有: ${message//the/The}" # 注意区分大小写
输出: The quick brown fox jumps over The lazy dog.
条件替换(如果开头匹配)
echo "开头替换: ${message/#The/A}"
输出: A quick brown fox jumps over the lazy dog.
条件替换(如果结尾匹配)
echo "结尾替换: ${message/%dog./fox.}"
输出: The quick brown fox jumps over the lazy fox.
字符串删除(替换为空)
echo "删除空格: ${message// /}"
输出: Thequickbrownfoxjumpsoverthelazydog.
二、核心文本处理工具:grep、sed、awk Shell 生态中有三个强大的文本处理工具,被称为 "三驾马车":
- grep:文本搜索工具 grep 用于在文本中搜索匹配指定模式的行: bash
基本用法:在文件中搜索关键词
grep "error" /var/log/syslog
忽略大小写
grep -i "error" /var/log/syslog
显示行号
grep -n "error" /var/log/syslog
显示不匹配的行
grep -v "info" /var/log/syslog
递归搜索目录中的所有文件
grep -r "function" /path/to/source/code
使用正则表达式
grep -E "^[0-9]{3}-[0-9]{3}-[0-9]{4}$" phone_numbers.txt 2. sed:流编辑器 sed 用于对文本进行流式编辑,可以实现替换、删除、插入等操作: bash
基本替换(只替换每行第一个匹配)
sed 's/old/new/' file.txt
全局替换
sed 's/old/new/g' file.txt
替换并修改原文件(谨慎使用)
sed -i 's/old/new/g' file.txt
删除空行
sed '/^$/d' file.txt
删除包含特定模式的行
sed '/error/d' logfile.txt
在匹配行前插入内容
sed '/^function/i\//注释: 函数定义' script.sh
在匹配行后插入内容
sed '/^exit 0/a\echo "脚本结束"' script.sh 3. awk:文本处理语言 awk 是功能强大的文本处理语言,特别适合处理结构化文本: bash
打印文件的第1和第3列
awk '{print $1, $3}' data.txt
按特定分隔符分割(如逗号)
awk -F ',' '{print $2, $4}' csvfile.csv
条件过滤(打印第3列大于100的行)
awk '$3 > 100 {print $0}' numbers.txt
计算列的总和
awk '{sum += $1} END {print "总和:", sum}' values.txt
统计单词出现次数
awk '{for(i=1;i<=NF;i++) count[$i]++} END {for(word in count) print word, count[word]}' text.txt
格式化输出
awk -F ',' 'BEGIN {print "姓名\t年龄"} {printf "%-10s\t%d\n", $1, $3}' people.csv 三、文本处理实战:组合工具解决复杂问题 强大的文本处理往往需要组合使用多个工具,通过管道(|)连接:
- 日志分析示例 分析 Web 服务器日志,找出访问量最高的 10 个 IP: bash
从 Apache 日志中提取 IP 地址并统计
cat /var/log/apache2/access.log | awk '{print $1}' | # 提取第一列(IP地址) sort | # 排序 uniq -c | # 统计出现次数 sort -nr | # 按次数逆序排序 head -n 10 # 取前10名 2. 数据转换示例 将 CSV 文件转换为 Markdown 表格: bash #!/bin/bash
csv2md.sh - 将 CSV 转换为 Markdown 表格
if [ $# -ne 1 ]; then echo "用法: $0 <input.csv>" exit 1 fi
处理表头
head -n 1 "$1" | sed 's/,/|/g' | awk '{print "|", $0, "|"}'
生成分隔线
head -n 1 "$1" | awk -F ',' '{for(i=1;i<=NF;i++) printf "|---"; print "|"}'
处理数据行
tail -n +2 "$1" | sed 's/,/|/g' | awk '{print "|", $0, "|"}' 使用方法:./csv2md.sh data.csv 3. 文本清洗示例 清理杂乱的文本数据,提取有用信息: bash
从混合文本中提取所有电子邮件地址
grep -E -o "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" messy_text.txt | sort | uniq > emails.txt
从日志中提取特定时间段的错误
grep "ERROR" /var/log/application.log | grep "2023-10-15 14:" | # 10月15日14点左右的错误 sed 's/.*ERROR: //' | # 只保留错误信息部分 sort | uniq -c | sort -nr > error_summary.txt 四、高级文本处理:正则表达式深入应用 正则表达式是文本处理的利器,掌握它能大幅提升处理能力:
- 基本正则表达式语法 元字符 含义 . 匹配任意单个字符
- 匹配前面的元素零次或多次
- 匹配前面的元素一次或多次(需加 - E 选项)
? 匹配前面的元素零次或一次(需加 - E 选项)
^ 匹配行首
$ 匹配行尾
[abc] 匹配 a、b 或 c
[a-z] 匹配任意小写字母
[^a-z] 匹配非小写字母
(ab cd)
匹配 ab 或 cd \b 匹配单词边界
- 实用正则表达式示例 bash
匹配 URL
grep -E -o "https?://[a-zA-Z0-9./?=_-]+" documents.txt
匹配 IPv4 地址
grep -E -o "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" network.log | grep -E -v "\b(0|25[6-9]|2[6-9][0-9]|[3-9][0-9][0-9])\." # 过滤无效IP
匹配日期(YYYY-MM-DD)
grep -E -o "\b20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\b" dates.txt
替换 HTML 标签
sed -E 's/<[^>]+>//g' webpage.html # 移除所有HTML标签 3. awk 中的复杂模式匹配 bash
提取 JSON 中的特定字段(简化版)
awk -F '[:,]' '{
for(i=1; i<=NF; i++) {
if ($i ~ /"name"/) {
gsub(/[" ]/, "", (i+1)
}
if ($i ~ /"email"/) {
gsub(/[" ]/, "",
(i+1)
}
}
if (name && email) {
print name ": " email
name=""
email=""
}
}' data.json
五、企业级文本处理脚本示例
下面是一个综合示例,展示企业级文本处理脚本的结构:
bash
#!/bin/bash
日志分析与报告生成工具
set -euo pipefail
配置
LOG_FILE="/var/log/application/server.log"
REPORT_DIR="./reports"
TODAY=REPORT_DIR/report_$TODAY.txt"
初始化
init() { echo "初始化日志分析工具..." mkdir -p "$REPORT_DIR"
# 检查日志文件
if [ ! -f "$LOG_FILE" ] || [ ! -r "$LOG_FILE" ]; thenecho "错误:日志文件 $LOG_FILE 不存在或不可读" >&2exit 1
fi
}
分析错误
analyze_errors() { echo "=== 错误分析 ===" >> "$REPORT_FILE"
# 统计错误类型
echo -e "\n错误类型统计:" >> "$REPORT_FILE"
grep -i "error" "$LOG_FILE" |awk -F 'ERROR: ' '{print $2}' |cut -d ' ' -f 1 |sort |uniq -c |sort -nr >> "$REPORT_FILE"# 提取最新的10个错误详情
echo -e "\n最新10个错误详情:" >> "$REPORT_FILE"
grep -in "error" "$LOG_FILE" |tail -n 10 >> "$REPORT_FILE"
}
分析性能
analyze_performance() { echo -e "\n\n=== 性能分析 ===" >> "$REPORT_FILE"
# 找出响应时间超过1秒的请求
echo -e "\n慢请求(>1s):" >> "$REPORT_FILE"
awk '/Response time/ && $NF > 1.0 {print $0}' "$LOG_FILE" |sort -kNF -nr |head -n 20 >> "$REPORT_FILE"
}
生成摘要
generate_summary() {
local total_lines=LOG_FILE")
local error_lines=
LOG_FILE")
local warn_lines=
LOG_FILE")
local info_lines=$((total_lines - error_lines - warn_lines))
echo "=== 日志分析报告 - $TODAY ===" > "$REPORT_FILE"
echo -e "\n总记录数:$total_lines" >> "$REPORT_FILE"
echo "信息记录:$info_lines" >> "$REPORT_FILE"
echo "警告记录:$warn_lines" >> "$REPORT_FILE"
echo "错误记录:$error_lines" >> "$REPORT_FILE"
echo "错误率:$((error_lines * 100 / total_lines))%" >> "$REPORT_FILE"
}
主函数
main() { init echo "开始分析日志文件: $LOG_FILE"
generate_summary
analyze_errors
analyze_performanceecho "分析完成,报告已生成:$REPORT_FILE"# 显示报告摘要
echo -e "\n报告摘要:"
grep -A 5 "=== 日志分析报告" "$REPORT_FILE"
}
启动程序
main "$@" 六、文本处理最佳实践 工具选择原则 简单匹配用 grep 简单替换用 sed 字段处理用 awk 复杂逻辑考虑组合使用 性能优化技巧 处理大文件时,优先使用 awk 而非 sed 或 grep 循环 避免不必要的管道操作 复杂处理考虑使用临时文件存储中间结果 可读性与可维护性 复杂的单行命令应拆分为多行并添加注释 常用的文本处理逻辑封装为函数 使用变量存储文件名和模式,便于修改 健壮性考虑 处理文本前检查文件是否存在且可读 考虑特殊字符(空格、引号、换行符)的处理 大型文本处理添加进度提示 扩展学习 学习 perl 处理更复杂的文本任务 掌握 jq 用于 JSON 处理 了解 xmlstarlet 用于 XML 处理 掌握这些文本处理技巧,能让你在日常工作中处理日志分析、数据转换、报告生成等任务时游刃有余。Shell 文本处理工具的组合使用,往往能以极少的代码实现强大的功能,这也是 Shell 脚本在系统管理和数据处理领域经久不衰的原因。