Linux Shell脚本编程与文本处理实战指南:掌握grep、sed、awk三剑客的核心技能
Orion K Lv6

Shell脚本语言的优势在于能够以最轻量级最快捷的速度处理Linux操作系统偏底层的业务。比如软件的自动化安装、更新版本,监控报警,日志分析等。本文将深入介绍Linux文本处理三剑客:grep、sed、awk的核心用法和实战技巧,帮助你掌握高效的文本处理技能。

1. Linux文本处理三剑客概述

1.1 三剑客简介

Linux文本处理三剑客是指grep、sed、awk这三个强大的文本处理工具:

  • grep:主要用于文本内容查找,支持正则表达式,擅长模式匹配和搜索
  • sed:全称Stream Editor,主要用于文本内容的编辑,默认只处理模式空间,不改变原数据
  • awk:主要用于文本内容的分析处理,也常用于处理数据,生成报告,非常适用于需要按列处理的数据

1.2 应用场景对比

工具 主要功能 适用场景 特点
grep 文本搜索 查找匹配的行 简单快速,支持正则
sed 文本编辑 替换、删除、插入 流式处理,不修改原文件
awk 数据处理 格式化输出、统计分析 功能最强大,支持编程

2. grep命令详解

2.1 基本语法

1
grep [选项] "模式" 文件名

2.2 常用参数详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 基本搜索参数
-i # 忽略大小写
-v # 反向匹配(显示不匹配的行)
-n # 显示行号
-c # 统计匹配的行数
-w # 匹配整个单词
-o # 仅显示匹配到的字符串

# 上下文显示参数
-A<数字> # 显示匹配行及其后N行
-B<数字> # 显示匹配行及其前N行
-C<数字> # 显示匹配行及其前后N行

# 高级参数
-E # 扩展正则表达式
-F # 固定字符串匹配(不使用正则)
-r # 递归搜索目录
-q # 静默模式(用于脚本判断)

2.3 实战案例

基础搜索示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建测试文件
cat > test.txt << 'EOF'
Linux is powerful
UNIX system
linux commands
Windows OS
MacOS system
EOF

# 基本搜索
grep "Linux" test.txt
# 输出:Linux is powerful

# 忽略大小写搜索
grep -i "linux" test.txt
# 输出:Linux is powerful
# linux commands

# 显示行号
grep -n "system" test.txt
# 输出:2:UNIX system
# 5:MacOS system

# 统计匹配行数
grep -c "system" test.txt
# 输出:2

# 反向匹配
grep -v "system" test.txt
# 输出:Linux is powerful
# linux commands
# Windows OS

上下文显示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建日志文件
cat > app.log << 'EOF'
2024-01-01 10:00:01 INFO Application started
2024-01-01 10:00:02 DEBUG Loading configuration
2024-01-01 10:00:03 INFO Configuration loaded
2024-01-01 10:00:04 ERROR Database connection failed
2024-01-01 10:00:05 DEBUG Retrying connection
2024-01-01 10:00:06 INFO Database connected
2024-01-01 10:00:07 INFO Application ready
EOF

# 显示错误及其后2行
grep -A2 "ERROR" app.log
# 输出:2024-01-01 10:00:04 ERROR Database connection failed
# 2024-01-01 10:00:05 DEBUG Retrying connection
# 2024-01-01 10:00:06 INFO Database connected

# 显示错误及其前后1行
grep -C1 "ERROR" app.log
# 输出:2024-01-01 10:00:03 INFO Configuration loaded
# 2024-01-01 10:00:04 ERROR Database connection failed
# 2024-01-01 10:00:05 DEBUG Retrying connection

正则表达式示例

1
2
3
4
5
6
7
8
9
10
11
# 匹配以数字开头的行
grep "^[0-9]" app.log

# 匹配包含ERROR或WARN的行
grep -E "ERROR|WARN" app.log

# 匹配IP地址模式
grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.log

# 匹配邮箱地址
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" contacts.txt

2.4 grep实用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/bin/bash
# log_analyzer.sh - 日志分析脚本

# 分析日志文件中的错误信息
analyze_errors() {
local log_file="$1"

if [ ! -f "$log_file" ]; then
echo "错误:日志文件 $log_file 不存在"
return 1
fi

echo "=== 日志错误分析报告 ==="
echo "文件:$log_file"
echo "分析时间:$(date)"
echo ""

# 统计各类日志级别
echo "日志级别统计:"
grep -o -E "(DEBUG|INFO|WARN|ERROR|FATAL)" "$log_file" | sort | uniq -c
echo ""

# 显示所有错误信息
echo "错误详情:"
grep -n -E "(ERROR|FATAL)" "$log_file"
echo ""

# 统计最近1小时的错误
local current_hour=$(date +"%Y-%m-%d %H")
echo "最近1小时错误数:"
grep "$current_hour" "$log_file" | grep -c -E "(ERROR|FATAL)"
echo ""

# 查找最频繁的错误
echo "最频繁的错误(前5个):"
grep -E "(ERROR|FATAL)" "$log_file" | \
sed 's/.*ERROR\|FATAL//' | \
sort | uniq -c | sort -nr | head -5
}

# 搜索特定IP的访问记录
search_ip_access() {
local access_log="$1"
local ip="$2"

echo "=== IP访问分析:$ip ==="

# 总访问次数
echo "总访问次数:"
grep "$ip" "$access_log" | wc -l

# 访问的URL
echo "访问的URL(前10个):"
grep "$ip" "$access_log" | \
awk '{print $7}' | sort | uniq -c | sort -nr | head -10

# 状态码分布
echo "状态码分布:"
grep "$ip" "$access_log" | \
awk '{print $9}' | sort | uniq -c | sort -nr

# 最近访问时间
echo "最近访问:"
grep "$ip" "$access_log" | tail -5
}

# 主函数
main() {
case "$1" in
"error")
analyze_errors "$2"
;;
"ip")
search_ip_access "$2" "$3"
;;
*)
echo "用法:"
echo " $0 error <log_file> - 分析错误日志"
echo " $0 ip <access_log> <ip> - 分析IP访问"
;;
esac
}

main "$@"

3. sed命令详解

3.1 基本语法

1
sed [选项] '命令' 文件名

3.2 常用命令和选项

1
2
3
4
5
6
7
8
9
10
11
12
13
# 基本选项
-n # 取消默认输出
-i # 直接修改文件
-e # 执行多个命令
-f # 从脚本文件读取命令

# 基本命令
s # 替换
d # 删除
p # 打印
a # 追加
i # 插入
c # 替换整行

3.3 实战案例

文本替换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建测试文件
cat > config.txt << 'EOF'
server_name=localhost
port=8080
database_host=127.0.0.1
database_port=3306
debug=true
EOF

# 基本替换(只替换每行第一个匹配)
sed 's/localhost/example.com/' config.txt
# 输出:server_name=example.com

# 全局替换(替换所有匹配)
sed 's/localhost/example.com/g' config.txt

# 忽略大小写替换
sed 's/TRUE/false/gi' config.txt

# 使用不同分隔符
sed 's|127.0.0.1|192.168.1.100|g' config.txt

# 替换并保存到新文件
sed 's/8080/9090/g' config.txt > new_config.txt

# 直接修改原文件
sed -i 's/debug=true/debug=false/g' config.txt

行操作示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 创建测试文件
cat > users.txt << 'EOF'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
EOF

# 删除包含nologin的行
sed '/nologin/d' users.txt

# 删除第2行
sed '2d' users.txt

# 删除第2到4行
sed '2,4d' users.txt

# 只显示第1到3行
sed -n '1,3p' users.txt

# 在第2行后插入新行
sed '2a\nnewuser:x:1001:1001:New User:/home/newuser:/bin/bash' users.txt

# 在第1行前插入新行
sed '1i\n# User accounts file' users.txt

# 替换第3行
sed '3c\nmodified:x:999:999:Modified User:/home/modified:/bin/bash' users.txt

高级替换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用正则表达式和反向引用
echo "2024-01-15" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
# 输出:15/01/2024

# 提取IP地址
echo "Server IP: 192.168.1.100 Port: 8080" | \
sed 's/.*IP: \([0-9.]\+\).*/\1/'
# 输出:192.168.1.100

# 多重替换
sed -e 's/foo/bar/g' -e 's/hello/hi/g' input.txt

# 条件替换(只在包含特定模式的行进行替换)
sed '/database/s/localhost/remote_host/g' config.txt

3.4 sed实用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/bin/bash
# config_manager.sh - 配置文件管理脚本

# 更新配置文件中的参数值
update_config() {
local config_file="$1"
local key="$2"
local value="$3"

if [ ! -f "$config_file" ]; then
echo "错误:配置文件 $config_file 不存在"
return 1
fi

# 备份原文件
cp "$config_file" "${config_file}.backup.$(date +%Y%m%d_%H%M%S)"

# 检查key是否存在
if grep -q "^$key=" "$config_file"; then
# 更新现有配置
sed -i "s/^$key=.*/$key=$value/" "$config_file"
echo "已更新配置:$key=$value"
else
# 添加新配置
echo "$key=$value" >> "$config_file"
echo "已添加配置:$key=$value"
fi
}

# 注释掉配置项
comment_config() {
local config_file="$1"
local key="$2"

sed -i "s/^$key=/#$key=/" "$config_file"
echo "已注释配置:$key"
}

# 取消注释配置项
uncomment_config() {
local config_file="$1"
local key="$2"

sed -i "s/^#$key=/$key=/" "$config_file"
echo "已取消注释:$key"
}

# 删除配置项
delete_config() {
local config_file="$1"
local key="$2"

sed -i "/^$key=/d" "$config_file"
echo "已删除配置:$key"
}

# 格式化配置文件
format_config() {
local config_file="$1"

# 删除空行和注释行,然后按key排序
sed '/^$/d; /^#/d' "$config_file" | sort > "${config_file}.formatted"
echo "格式化完成:${config_file}.formatted"
}

# 主函数
main() {
case "$1" in
"update")
update_config "$2" "$3" "$4"
;;
"comment")
comment_config "$2" "$3"
;;
"uncomment")
uncomment_config "$2" "$3"
;;
"delete")
delete_config "$2" "$3"
;;
"format")
format_config "$2"
;;
*)
echo "用法:"
echo " $0 update <file> <key> <value> - 更新配置"
echo " $0 comment <file> <key> - 注释配置"
echo " $0 uncomment <file> <key> - 取消注释"
echo " $0 delete <file> <key> - 删除配置"
echo " $0 format <file> - 格式化文件"
;;
esac
}

main "$@"

4. awk命令详解

4.1 基本语法

1
2
awk '条件 {动作}' 文件名
awk -F '分隔符' '条件 {动作}' 文件名

4.2 内置变量

1
2
3
4
5
6
7
8
9
$0          # 整行内容
$1, $2, ... # 第1列、第2列等
NR # 当前记录号(行号)
NF # 字段数量
FS # 字段分隔符
OFS # 输出字段分隔符
RS # 记录分隔符
ORS # 输出记录分隔符
FILENAME # 当前文件名

4.3 实战案例

基础操作示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 创建测试数据
cat > sales.csv << 'EOF'
Name,Department,Salary,Bonus
John,IT,5000,500
Mary,HR,4500,300
Bob,IT,5500,600
Alice,Finance,4800,400
Tom,IT,5200,550
EOF

# 打印所有行
awk '{print}' sales.csv

# 打印第1列和第3列
awk -F',' '{print $1, $3}' sales.csv

# 打印行号和内容
awk '{print NR, $0}' sales.csv

# 打印字段数量
awk -F',' '{print NF}' sales.csv

# 跳过标题行,打印数据
awk -F',' 'NR>1 {print $1, $3}' sales.csv

条件筛选示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 筛选IT部门的员工
awk -F',' '$2=="IT" {print $1, $3}' sales.csv

# 筛选薪水大于5000的员工
awk -F',' '$3>5000 {print $1, $3}' sales.csv

# 筛选薪水在4500-5200之间的员工
awk -F',' '$3>=4500 && $3<=5200 {print $1, $3}' sales.csv

# 筛选姓名包含"o"的员工
awk -F',' '$1~/o/ {print $1, $2}' sales.csv

# 筛选不是HR部门的员工
awk -F',' '$2!="HR" {print $1, $2}' sales.csv

统计计算示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 计算总薪水
awk -F',' 'NR>1 {sum+=$3} END {print "总薪水:", sum}' sales.csv

# 计算平均薪水
awk -F',' 'NR>1 {sum+=$3; count++} END {print "平均薪水:", sum/count}' sales.csv

# 按部门统计人数
awk -F',' 'NR>1 {dept[$2]++} END {for(d in dept) print d, dept[d]}' sales.csv

# 按部门统计薪水总和
awk -F',' 'NR>1 {dept[$2]+=$3} END {for(d in dept) print d, dept[d]}' sales.csv

# 找出最高薪水
awk -F',' 'NR>1 {if($3>max) max=$3} END {print "最高薪水:", max}' sales.csv

# 统计各薪水范围的人数
awk -F',' 'NR>1 {
if($3<4000) low++;
else if($3<5000) mid++;
else high++;
} END {
print "低薪(<4000):", low;
print "中薪(4000-5000):", mid;
print "高薪(>5000):", high;
}' sales.csv

格式化输出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 格式化表格输出
awk -F',' '{
printf "%-10s %-10s %8s %8s\n", $1, $2, $3, $4
}' sales.csv

# 添加表头和分隔线
awk -F',' 'BEGIN {
printf "%-10s %-10s %8s %8s\n", "Name", "Dept", "Salary", "Bonus";
printf "%-10s %-10s %8s %8s\n", "----", "----", "------", "-----";
} NR>1 {
printf "%-10s %-10s %8d %8d\n", $1, $2, $3, $4;
}' sales.csv

# 计算总薪酬并格式化
awk -F',' 'NR>1 {
total = $3 + $4;
printf "%-10s: 基本薪水=%d, 奖金=%d, 总计=%d\n", $1, $3, $4, total;
}' sales.csv

4.4 awk实用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/bin/bash
# data_processor.sh - 数据处理脚本

# 处理访问日志
process_access_log() {
local log_file="$1"

echo "=== 访问日志分析 ==="

# 统计访问次数最多的IP(前10个)
echo "访问次数最多的IP:"
awk '{print $1}' "$log_file" | sort | uniq -c | sort -nr | head -10
echo ""

# 统计访问次数最多的页面(前10个)
echo "访问次数最多的页面:"
awk '{print $7}' "$log_file" | sort | uniq -c | sort -nr | head -10
echo ""

# 统计状态码分布
echo "状态码分布:"
awk '{print $9}' "$log_file" | sort | uniq -c | sort -nr
echo ""

# 统计每小时访问量
echo "每小时访问量:"
awk '{
# 提取时间戳中的小时
match($4, /\[.*:([0-9]{2}):/, arr);
hour[arr[1]]++;
} END {
for(h=0; h<24; h++) {
printf "%02d:00 - %d 次访问\n", h, hour[sprintf("%02d", h)];
}
}' "$log_file"
}

# 处理CSV数据
process_csv_data() {
local csv_file="$1"
local delimiter="${2:-,}"

echo "=== CSV数据分析 ==="

# 显示基本信息
echo "文件信息:"
echo "总行数:$(wc -l < "$csv_file")"
echo "字段数:$(head -1 "$csv_file" | awk -F"$delimiter" '{print NF}')"
echo ""

# 显示字段名
echo "字段名:"
head -1 "$csv_file" | awk -F"$delimiter" '{
for(i=1; i<=NF; i++) {
printf "%d: %s\n", i, $i;
}
}'
echo ""

# 数据预览(前5行)
echo "数据预览:"
head -6 "$csv_file" | awk -F"$delimiter" '{
for(i=1; i<=NF; i++) {
printf "%-15s ", $i;
}
printf "\n";
}'
}

# 生成数据报告
generate_report() {
local data_file="$1"
local report_file="${2:-report.txt}"

{
echo "=== 数据分析报告 ==="
echo "生成时间:$(date)"
echo "数据文件:$data_file"
echo ""

# 基本统计
echo "基本统计:"
awk '{
lines++;
chars += length($0);
words += NF;
} END {
printf "总行数:%d\n", lines;
printf "总字符数:%d\n", chars;
printf "总单词数:%d\n", words;
printf "平均行长度:%.2f\n", chars/lines;
printf "平均每行单词数:%.2f\n", words/lines;
}' "$data_file"

echo ""
echo "详细分析:"

# 行长度分布
awk '{
len = length($0);
if(len < 50) short++;
else if(len < 100) medium++;
else long++;
} END {
printf "短行(<50字符):%d\n", short;
printf "中行(50-100字符):%d\n", medium;
printf "长行(>100字符):%d\n", long;
}' "$data_file"

} > "$report_file"

echo "报告已生成:$report_file"
}

# 主函数
main() {
case "$1" in
"access")
process_access_log "$2"
;;
"csv")
process_csv_data "$2" "$3"
;;
"report")
generate_report "$2" "$3"
;;
*)
echo "用法:"
echo " $0 access <log_file> - 分析访问日志"
echo " $0 csv <csv_file> [delimiter] - 分析CSV数据"
echo " $0 report <data_file> [output] - 生成数据报告"
;;
esac
}

main "$@"

5. 三剑客组合使用

5.1 管道组合技巧

1
2
3
4
5
6
7
8
9
10
11
12
# 查找错误日志并统计
grep "ERROR" app.log | awk '{print $1}' | sort | uniq -c

# 提取IP地址并分析
grep -o -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.log | \
sort | uniq -c | sort -nr | head -10

# 处理配置文件
grep -v "^#" config.conf | sed '/^$/d' | awk -F'=' '{print $1}'

# 分析系统进程
ps aux | grep -v "grep" | awk '$3>10 {print $2, $11}' | sort -k1 -nr

5.2 复杂数据处理示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/bin/bash
# complex_analysis.sh - 复杂数据分析脚本

# 分析Web服务器日志
analyze_web_logs() {
local log_file="$1"
local output_dir="${2:-./analysis}"

mkdir -p "$output_dir"

echo "开始分析Web日志:$log_file"

# 1. 提取并分析IP访问模式
echo "分析IP访问模式..."
grep -o -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" "$log_file" | \
sort | uniq -c | sort -nr | \
awk '{
if($1 > 1000) level="高频";
else if($1 > 100) level="中频";
else level="低频";
printf "%-15s %6d次 [%s]\n", $2, $1, level;
}' > "$output_dir/ip_analysis.txt"

# 2. 分析状态码分布
echo "分析状态码分布..."
awk '{print $9}' "$log_file" | \
grep -E "^[0-9]{3}$" | \
sort | uniq -c | sort -nr | \
awk '{
if($2 ~ /^2/) status="成功";
else if($2 ~ /^3/) status="重定向";
else if($2 ~ /^4/) status="客户端错误";
else if($2 ~ /^5/) status="服务器错误";
else status="其他";
printf "%-3s %-12s %6d次\n", $2, status, $1;
}' > "$output_dir/status_analysis.txt"

# 3. 分析访问时间模式
echo "分析访问时间模式..."
sed -n 's/.*\[\([^:]*\):\([0-9]\{2\}\):.*/\1 \2/p' "$log_file" | \
awk '{
date_hour[$1" "$2]++;
hour[$2]++;
} END {
print "=== 每小时访问统计 ===" > "'$output_dir'/time_analysis.txt";
for(h=0; h<24; h++) {
printf "%02d:00 %6d次\n", h, hour[sprintf("%02d", h)] > "'$output_dir'/time_analysis.txt";
}
print "\n=== 每日每小时详细统计 ===" >> "'$output_dir'/time_analysis.txt";
for(dh in date_hour) {
printf "%-20s %6d次\n", dh, date_hour[dh] >> "'$output_dir'/time_analysis.txt";
}
}'

# 4. 生成综合报告
echo "生成综合报告..."
{
echo "=== Web日志分析报告 ==="
echo "分析时间:$(date)"
echo "日志文件:$log_file"
echo "总访问量:$(wc -l < "$log_file")"
echo ""

echo "=== TOP 10 访问IP ==="
head -10 "$output_dir/ip_analysis.txt"
echo ""

echo "=== 状态码分布 ==="
cat "$output_dir/status_analysis.txt"
echo ""

echo "=== 访问高峰时段 ==="
head -5 "$output_dir/time_analysis.txt"

} > "$output_dir/summary_report.txt"

echo "分析完成,结果保存在:$output_dir"
}

# 处理CSV销售数据
process_sales_data() {
local csv_file="$1"

echo "=== 销售数据分析 ==="

# 数据清洗和验证
echo "数据清洗中..."
grep -v "^$" "$csv_file" | \
sed '1d' | \
awk -F',' '{
# 验证数据完整性
if(NF >= 4 && $3 ~ /^[0-9]+$/ && $4 ~ /^[0-9]+$/) {
print $0;
} else {
print "无效数据行:" $0 > "/dev/stderr";
}
}' > temp_clean_data.csv

# 统计分析
awk -F',' '{
# 按部门统计
dept_count[$2]++;
dept_salary[$2] += $3;
dept_bonus[$2] += $4;

# 总体统计
total_salary += $3;
total_bonus += $4;
total_count++;

# 薪资范围统计
if($3 < 4000) low_salary++;
else if($3 < 5000) mid_salary++;
else high_salary++;

} END {
print "=== 总体统计 ===";
printf "员工总数:%d\n", total_count;
printf "平均薪资:%.2f\n", total_salary/total_count;
printf "平均奖金:%.2f\n", total_bonus/total_count;
printf "薪资总额:%d\n", total_salary;
printf "奖金总额:%d\n", total_bonus;

print "\n=== 薪资分布 ===";
printf "低薪(<4000):%d人\n", low_salary;
printf "中薪(4000-5000):%d人\n", mid_salary;
printf "高薪(>5000):%d人\n", high_salary;

print "\n=== 部门统计 ===";
for(dept in dept_count) {
printf "%-10s: %2d人, 平均薪资:%.2f, 平均奖金:%.2f\n",
dept, dept_count[dept],
dept_salary[dept]/dept_count[dept],
dept_bonus[dept]/dept_count[dept];
}
}' temp_clean_data.csv

# 清理临时文件
rm -f temp_clean_data.csv
}

# 主函数
main() {
case "$1" in
"web")
analyze_web_logs "$2" "$3"
;;
"sales")
process_sales_data "$2"
;;
*)
echo "用法:"
echo " $0 web <log_file> [output_dir] - 分析Web日志"
echo " $0 sales <csv_file> - 分析销售数据"
;;
esac
}

main "$@"

6. 性能优化和最佳实践

6.1 性能优化技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 使用合适的工具
# 简单搜索用grep,复杂处理用awk
grep "pattern" file.txt # 快速
awk '/pattern/' file.txt # 较慢但功能强大

# 2. 减少不必要的处理
# 只处理需要的行
awk 'NR<=1000 {print $1}' large_file.txt

# 3. 使用内置函数
# 避免外部命令调用
awk '{gsub(/old/, "new"); print}' file.txt # 好
awk '{print}' file.txt | sed 's/old/new/g' # 不好

# 4. 合理使用正则表达式
# 简单匹配用固定字符串
grep -F "fixed_string" file.txt # 快
grep "fixed_string" file.txt # 慢

6.2 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/bin/bash
# best_practices.sh - 最佳实践示例

# 1. 错误处理
process_file_safely() {
local file="$1"

# 检查文件是否存在
if [ ! -f "$file" ]; then
echo "错误:文件 $file 不存在" >&2
return 1
fi

# 检查文件是否可读
if [ ! -r "$file" ]; then
echo "错误:文件 $file 不可读" >&2
return 1
fi

# 处理文件
awk '{print NR, $0}' "$file" || {
echo "错误:处理文件 $file 失败" >&2
return 1
}
}

# 2. 参数验证
validate_parameters() {
local pattern="$1"
local file="$2"

if [ -z "$pattern" ]; then
echo "错误:未指定搜索模式" >&2
return 1
fi

if [ -z "$file" ]; then
echo "错误:未指定文件" >&2
return 1
fi

return 0
}

# 3. 资源清理
cleanup_temp_files() {
local temp_dir="$1"

if [ -d "$temp_dir" ]; then
rm -rf "$temp_dir"
echo "已清理临时目录:$temp_dir"
fi
}

# 4. 进度显示
process_large_file() {
local file="$1"
local total_lines=$(wc -l < "$file")
local processed=0

while IFS= read -r line; do
# 处理每一行
echo "$line" | awk '{print toupper($0)}'

# 更新进度
((processed++))
if ((processed % 1000 == 0)); then
printf "\r进度: %d/%d (%.1f%%)" \
"$processed" "$total_lines" \
"$(echo "scale=1; $processed*100/$total_lines" | bc)"
fi
done < "$file"

echo "\n处理完成"
}

# 5. 配置管理
load_config() {
local config_file="${1:-./config.conf}"

if [ -f "$config_file" ]; then
# 安全地加载配置
while IFS='=' read -r key value; do
# 跳过注释和空行
[[ $key =~ ^[[:space:]]*# ]] && continue
[[ -z $key ]] && continue

# 设置变量
declare -g "$key"="$value"
done < "$config_file"

echo "配置已加载:$config_file"
else
echo "警告:配置文件 $config_file 不存在,使用默认配置"
fi
}

# 主函数示例
main() {
# 设置错误处理
set -euo pipefail

# 设置临时目录
local temp_dir=$(mktemp -d)
trap "cleanup_temp_files '$temp_dir'" EXIT

# 加载配置
load_config

# 处理参数
if ! validate_parameters "$@"; then
echo "用法: $0 <pattern> <file>"
exit 1
fi

# 执行主要逻辑
process_file_safely "$2"
}

# 如果直接执行脚本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

7. 总结

7.1 核心要点

  1. 工具选择

    • grep:快速文本搜索和模式匹配
    • sed:流式文本编辑和替换
    • awk:强大的数据处理和格式化
  2. 组合使用

    • 通过管道连接多个命令
    • 发挥各工具的优势
    • 实现复杂的数据处理流程
  3. 性能考虑

    • 选择合适的工具
    • 优化正则表达式
    • 减少不必要的处理

7.2 学习建议

  1. 循序渐进:从基本用法开始,逐步掌握高级特性
  2. 实践为主:通过实际项目练习和应用
  3. 组合思维:学会将多个工具组合使用
  4. 性能意识:关注处理效率和资源使用

7.3 进阶方向

  • 学习更多正则表达式技巧
  • 掌握Shell脚本编程
  • 了解其他文本处理工具(如cut、sort、uniq等)
  • 学习数据处理和分析方法

通过掌握Linux文本处理三剑客,你将能够高效地处理各种文本数据,为系统管理、日志分析、数据处理等工作提供强有力的支持。

本站由 提供部署服务