52、高级 sed 与 gawk 实用指南

高级 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 都能发挥重要的作用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值