Shell 脚本中的文本处理:从基础到高级的实战技巧 文本处理是 Shell 脚本最擅长的领域之一。从简单的日志分析到复杂的数据转换,掌握文本处理技巧能让你轻松应对各种场景。本文将系统介绍 Shell 环境中常用的文本处理工具和技术,从基础的字符串操作到高级的正则表达式应用,帮助你成为 Shell 文本处理高手。 一、字符串基础操作:变量与字符串处理 在 Shell 中,字符串是最基本的数据类型,掌握其操作是文本处理的基础。

  1. 字符串变量的创建与拼接 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 Shell 脚本中的文本处理:从基础到高级的实战技巧_文本处理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 生态中有三个强大的文本处理工具,被称为 "三驾马车":

  1. 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 三、文本处理实战:组合工具解决复杂问题 强大的文本处理往往需要组合使用多个工具,通过管道(|)连接:

  1. 日志分析示例 分析 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 四、高级文本处理:正则表达式深入应用 正则表达式是文本处理的利器,掌握它能大幅提升处理能力:

  1. 基本正则表达式语法 元字符 含义 . 匹配任意单个字符
  • 匹配前面的元素零次或多次
  • 匹配前面的元素一次或多次(需加 - E 选项) ? 匹配前面的元素零次或一次(需加 - E 选项) ^ 匹配行首 $ 匹配行尾 [abc] 匹配 a、b 或 c [a-z] 匹配任意小写字母 [^a-z] 匹配非小写字母 (ab cd) 匹配 ab 或 cd \b 匹配单词边界
  1. 实用正则表达式示例 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(/[" ]/, "", Shell 脚本中的文本处理:从基础到高级的实战技巧_bash_02(i+1) } if ($i ~ /"email"/) { gsub(/[" ]/, "", Shell 脚本中的文本处理:从基础到高级的实战技巧_文本处理_03(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=Shell 脚本中的文本处理:从基础到高级的实战技巧_bash_04REPORT_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=Shell 脚本中的文本处理:从基础到高级的实战技巧_sed_05LOG_FILE") local error_lines=Shell 脚本中的文本处理:从基础到高级的实战技巧_文本处理_06LOG_FILE") local warn_lines=Shell 脚本中的文本处理:从基础到高级的实战技巧_sed_07LOG_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 脚本在系统管理和数据处理领域经久不衰的原因。