高级 sed 与 gawk 实用指南
1. 高级 sed 应用
在处理文本时,我们常常需要对文本进行清理和格式化。例如,当我们处理包含 HTML 标签的文本时,可能希望去除这些标签。可以使用如下命令:
$ sed 's/<[^>]*>//g ; /^$/d' data11.txt
这个命令的作用是先使用正则表达式
s/<[^>]*>//g
去除所有 HTML 标签,再使用
/^$/d
删除所有空白行。执行该命令后,输出的文本将更加简洁,只包含我们需要查看的数据。
1.1 实用示例:扫描 Bash 脚本
在实际应用中,我们可以使用 sed 来扫描 Bash 脚本,目的是找出那些可能适合封装在函数中的重复命令。以一个包含重复代码块的文本文件
ScriptData.txt
为例:
$ cat ScriptData.txt
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 3
Line 4
Line 5
Line 7
Line 8
Line 3
Line 4
Line 5
Line 9
Line 10
Line 11
Line 12
$
可以看到,
Line 3
、
Line 4
和
Line 5
这三行代码重复出现了三次。为了找出这些重复的行,我们可以采用以下步骤:
1. 使用多行
N
命令读取下一行,但仅在处理文本文件的第一行时执行。
2. 再次使用多行
N
命令读取下一行,此时模式空间中包含了三行文本。
3. 将模式空间的内容打印到标准输出。
4. 使用
D
命令删除模式空间中的第一行,即删除模式空间中直到并包含第一个换行符的文本,然后重新开始处理 sed 脚本命令。
以下是实现该过程的命令:
$ sed -n '{
> 1N
> N
> p
> D}
> ' ScriptData.txt
然而,这种方法的输出结果包含换行符,人类很难区分哪些行是合并在一起的。为了解决这个问题,我们可以使用替换命令
s
将每行文本末尾的换行符替换为响铃字符
\a
:
$ sed -n '{
> 1N
> N
> s/\n/\a/g
> p
> s/\a/\n/
> D}
> ' ScriptData.txt
此外,shell 脚本中的行通常包含空格或制表符,这些会干扰匹配过程。我们可以使用全局替换命令
s
来消除这些空格和制表符:
$ sed -n '{
> 1N
> N
> s/ //g
> s/\t//g
> s/\n/\a/g
> p
> s/\a/\n/
> D}
> ' ScriptDataB.txt
最后,我们可以结合
sort
和
uniq -d
命令来排序文件并找出重复的行:
$ sed -n '{
> 1N;
> N;
> s/ //g;
> s/\t//g;
> s/\n/\a/g;
> p
> s/\a/\n/;
> D}
> ' ScriptDataB.txt |
> sort | uniq -d |
> sed 's/\a/\n/g'
为了方便使用,我们可以将上述命令封装在一个 shell 脚本中:
$ cat NeededFunctionCheck.sh
#!/bin/bash
# Checks for 3 duplicate lines in scripts.
# Suggest these lines be possibly replaced
# by a function.
#
tempfile=$2
#
#
sed -n '{
1N; N;
s/ //g; s/\t//g;
s/\n/\a/g; p;
s/\a/\n/; D}' $1 >> $tempfile
#
sort $tempfile | uniq -d | sed 's/\a/\n/g'
#
rm -i $tempfile
#
exit
$
这个脚本接受两个参数:第一个参数
$1
是要扫描的文件,第二个参数
$2
是用于存储合并后文件行的临时文件。
2. 高级 gawk 编程
gawk 是一种功能强大的文本处理工具,它支持许多高级编程语言的特性,可以用于编写复杂的数据处理和报告程序。
2.1 使用变量
gawk 支持两种类型的变量:内置变量和用户自定义变量。
2.1.1 内置变量
gawk 使用内置变量来引用程序数据中的特定特征。以下是一些常见的内置变量:
| 变量 | 描述 |
|---|---|
FIELDWIDTHS
| 一个以空格分隔的数字列表,定义每个数据字段的精确宽度(以空格为单位) |
FS
| 输入字段分隔符字符 |
RS
| 输入记录分隔符字符 |
OFS
| 输出字段分隔符字符 |
ORS
| 输出记录分隔符字符 |
ARGC
| 命令行参数的数量 |
ARGIND
|
当前正在处理的文件在
ARGV
数组中的索引
|
ARGV
| 命令行参数数组 |
CONVFMT
|
数字的转换格式(参见
printf
语句),默认值为
%.6 g
|
ENVIRON
| 当前 shell 环境变量及其值的关联数组 |
ERRNO
| 读取或关闭输入文件时发生错误的系统错误信息 |
FILENAME
| 用于 gawk 程序输入的数据文件的文件名 |
FNR
| 当前数据文件中的记录号 |
IGNORECASE
| 如果设置为非零值,则在 gawk 命令中忽略字符串的大小写 |
NF
| 数据文件中的数据字段总数 |
NR
| 已处理的输入记录数 |
OFMT
|
显示数字的输出格式,默认值为
%.6g
,以浮点数或科学记数法显示值,取较短的形式,最多保留六位小数
|
RLENGTH
| 匹配函数中匹配的子字符串的长度 |
RSTART
| 匹配函数中匹配的子字符串的起始索引 |
以下是一些使用内置变量的示例:
$ cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
$ gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1
data11 data12 data13
data21 data22 data23
data31 data32 data33
$
在这个示例中,我们使用
FS
变量将字段分隔符设置为逗号,并打印出每行的前三个字段。
$ cat data1b
1005.3247596.37
115-2.349194.00
05810.1298100.1
$ gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1b
100 5.324 75 96.37
115 -2.34 91 94.00
058 10.12 98 100.1
$
在这个示例中,我们使用
FIELDWIDTHS
变量根据字段宽度来解析数据记录。
$ gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
$
在这个示例中,我们使用
ARGC
和
ARGV
变量来获取命令行参数的数量和值。
$ gawk '
> BEGIN{
> print ENVIRON["HOME"]
> print ENVIRON["PATH"]
> }'
/home/rich
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/
games:/usr/local/games:/snap/bin
$
在这个示例中,我们使用
ENVIRON
变量来获取 shell 环境变量的值。
$ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin
sys:/usr/sbin/nologin
sync:/bin/sync
games:/usr/sbin/nologin
man:/usr/sbin/nologin
...
rich:/bin/bash
$
在这个示例中,我们使用
NF
变量来获取每行的最后一个字段。
$ gawk 'BEGIN{FS=","}{print $1,"FNR="FNR}' data1 data1
data11 FNR=1
data21 FNR=2
data31 FNR=3
data11 FNR=1
data21 FNR=2
data31 FNR=3
$
$ gawk '
> BEGIN {FS=","}
> {print $1,"FNR="FNR,"NR="NR}
> END{print "There were",NR,"records processed"}' data1 data1
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
data11 FNR=1 NR=4
data21 FNR=2 NR=5
data31 FNR=3 NR=6
There were 6 records processed
$
在这两个示例中,我们可以看到
FNR
和
NR
变量的区别:
FNR
表示当前数据文件中的记录号,而
NR
表示已处理的输入记录数。
通过合理使用这些内置变量,我们可以更加灵活地处理和分析文本数据。
下面是一个 mermaid 流程图,展示了使用 gawk 处理数据的基本流程:
graph LR
A[开始] --> B[设置变量]
B --> C[读取数据文件]
C --> D[处理数据记录]
D --> E{是否还有记录}
E -- 是 --> D
E -- 否 --> F[输出结果]
F --> G[结束]
这个流程图清晰地展示了使用 gawk 处理数据的主要步骤,从开始设置变量,到读取数据文件,逐行处理记录,最后输出结果。在实际应用中,我们可以根据具体需求在处理数据记录的步骤中添加更多的操作,如使用内置变量进行数据筛选、计算等。
高级 sed 与 gawk 实用指南
2. 高级 gawk 编程(续)
2.2 处理数组
在 gawk 中,数组是一种非常有用的数据结构,它可以用来存储和处理多个值。gawk 支持两种类型的数组:数字索引数组和关联数组。
2.2.1 数字索引数组
数字索引数组使用数字作为索引来访问数组元素。以下是一个简单的示例:
$ gawk '
BEGIN {
array[1] = "apple"
array[2] = "banana"
array[3] = "cherry"
for (i = 1; i <= 3; i++) {
print array[i]
}
}'
在这个示例中,我们创建了一个数字索引数组
array
,并为其三个元素赋值。然后使用
for
循环遍历数组并打印每个元素的值。
2.2.2 关联数组
关联数组使用字符串作为索引来访问数组元素。以下是一个示例:
$ gawk '
BEGIN {
fruit["red"] = "apple"
fruit["yellow"] = "banana"
fruit["red"] = "cherry"
for (color in fruit) {
print color ": " fruit[color]
}
}'
在这个示例中,我们创建了一个关联数组
fruit
,使用颜色作为索引,水果名称作为值。然后使用
for...in
循环遍历数组并打印每个元素的索引和值。
2.3 考虑模式
gawk 中的模式用于匹配数据记录,从而决定哪些记录需要进行处理。常见的模式包括正则表达式模式、范围模式和条件模式。
2.3.1 正则表达式模式
正则表达式模式使用正则表达式来匹配记录。以下是一个示例:
$ gawk '/^data/ {print $0}' data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
在这个示例中,正则表达式
^data
用于匹配以
data
开头的记录,并打印这些记录。
2.3.2 范围模式
范围模式用于匹配从一个模式到另一个模式之间的记录。以下是一个示例:
$ gawk '/data11/, /data21/ {print $0}' data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
在这个示例中,范围模式
/data11/, /data21/
用于匹配从包含
data11
的记录到包含
data21
的记录之间的所有记录,并打印这些记录。
2.3.3 条件模式
条件模式使用条件语句来决定是否匹配记录。以下是一个示例:
$ gawk '$1 == "data11" {print $0}' data1
data11,data12,data13,data14,data15
在这个示例中,条件模式
$1 == "data11"
用于匹配第一个字段等于
data11
的记录,并打印这些记录。
2.4 结构化命令
gawk 支持多种结构化命令,如
if-else
语句、
while
循环、
for
循环等,这些命令可以用于控制程序的流程。
2.4.1 if-else 语句
if-else
语句用于根据条件执行不同的代码块。以下是一个示例:
$ gawk '{
if ($1 == "data11") {
print "This is data11 record"
} else {
print "This is not data11 record"
}
}' data1
在这个示例中,如果第一个字段等于
data11
,则打印
This is data11 record
,否则打印
This is not data11 record
。
2.4.2 while 循环
while
循环用于在条件为真时重复执行代码块。以下是一个示例:
$ gawk '
BEGIN {
i = 1
while (i <= 3) {
print i
i++
}
}'
在这个示例中,
while
循环会在
i
小于等于 3 时重复执行,每次循环打印
i
的值并将
i
加 1。
2.4.3 for 循环
for
循环也用于重复执行代码块。以下是一个示例:
$ gawk '
BEGIN {
for (i = 1; i <= 3; i++) {
print i
}
}'
这个示例中的
for
循环与
while
循环的功能相同,都是打印 1 到 3 的数字。
2.5 格式化打印
gawk 提供了
printf
命令用于格式化输出。
printf
命令的语法与 C 语言中的
printf
函数类似。以下是一个示例:
$ gawk '
BEGIN {
name = "John"
age = 30
printf "Name: %s, Age: %d\n", name, age
}'
在这个示例中,
printf
命令使用
%s
表示字符串,
%d
表示整数,将
name
和
age
的值格式化输出。
2.6 使用内置函数
gawk 提供了许多内置函数,用于执行各种操作,如字符串处理、数学计算等。以下是一些常见的内置函数示例:
| 函数 | 描述 | 示例 |
|---|---|---|
length()
| 返回字符串的长度 |
gawk '{print length($1)}' data1
|
substr()
| 返回字符串的子字符串 |
gawk '{print substr($1, 1, 3)}' data1
|
tolower()
| 将字符串转换为小写 |
gawk '{print tolower($1)}' data1
|
toupper()
| 将字符串转换为大写 |
gawk '{print toupper($1)}' data1
|
sqrt()
| 计算平方根 |
gawk '{print sqrt(9)}'
|
sin()
| 计算正弦值 |
gawk '{print sin(0)}'
|
2.7 尝试用户自定义函数
除了使用内置函数,gawk 还允许用户自定义函数。以下是一个简单的用户自定义函数示例:
$ gawk '
function add(a, b) {
return a + b
}
BEGIN {
result = add(3, 5)
print result
}'
在这个示例中,我们定义了一个名为
add
的函数,用于计算两个数的和。然后在
BEGIN
块中调用该函数并打印结果。
下面是一个 mermaid 流程图,展示了使用 gawk 进行数据处理和分析的完整流程:
graph LR
A[开始] --> B[设置变量和数组]
B --> C[读取数据文件]
C --> D[应用模式匹配记录]
D --> E{是否匹配}
E -- 是 --> F[执行结构化命令]
F --> G[格式化打印结果]
G --> H{是否还有记录}
H -- 是 --> D
H -- 否 --> I[结束]
E -- 否 --> H
这个流程图展示了使用 gawk 进行数据处理和分析的完整过程,从开始设置变量和数组,到读取数据文件,使用模式匹配记录,根据匹配结果执行结构化命令,格式化打印结果,最后判断是否还有记录需要处理。通过这个流程,我们可以利用 gawk 的各种功能对文本数据进行全面的处理和分析。
综上所述,sed 和 gawk 是非常强大的文本处理工具,通过掌握它们的高级特性,如 sed 的多行处理和 gawk 的变量、数组、模式匹配、结构化命令等,我们可以更加高效地处理和分析各种文本数据,满足不同的应用需求。无论是清理 HTML 标签、扫描 Bash 脚本中的重复代码,还是编写复杂的数据处理和报告程序,sed 和 gawk 都能发挥重要的作用。
超级会员免费看
55

被折叠的 条评论
为什么被折叠?



