文章目录
一、awk和grep比较
二、awk语法
三 awk变量
四、awk简单使用示例
4.1 分割文本
4.2 搜索并替换文本
4.3 数值计算
五、使用awk脚本
5.1 awk脚本的基本用法
5.2 进阶知识点
5.21 awk语句
5.22 awk运算
5.23 awk高级输入输出
5.24 awk函数
5.3 复杂awk脚本示例
六、为什么使用awk
awk是一种基于文本的编程语言,也是一种强大的文本处理工具。它通常用于批处理、文本挖掘和数据处理等任务,尤其对于需要对大型文件进行处理的情况,awk的速度和效率很高。awk提供了多种内置功能,例如:处理和转换文本、过滤器、数据匹配、聚合和计算等等,使得它可以快速处理大量的数据,灵活性是awk最大的优势。awk经过改进生成的新的版本nawk,gawk,现在默认linux系统下日常使用的是gawk。
一、awk和grep比较
上一篇文章已经介绍过grep:【Linux进阶命令 01】grep(文本的全局搜索与打印)
grep是一种基于模式匹配的搜索工具,可以帮助用户在文本文件中查找特定的字符串。grep可以很容易地筛选出某个字符串,从而快速筛选出满足条件的行,并将其输出到终端或另一个文件中。grep的优点是简单易用,用法非常简单。 缺点是 grep 只能进行过滤和搜索,不能进行更复杂的操作。
awk除了支持查找和过滤文本之外,还提供了更强大的功能。Awk可以命令行式组织和操作数据,例如分割文件单元、替换文本、进行计算、格式化输出等等。Awk还拥有更强大的处理和过滤文本的能力,包括使用正则表达式进行高级匹配,将多个文件或变量连接,从不同数据源聚合数据,从文本字段中提取数据等。Awk就像一种专门用于文本处理的程序语言,可以轻松处理数据文件中的各种计算和逻辑操作。
二、awk语法
awk命令的基本语法如下:
awk [选项] 'pattern {action}' [文件名]
常用的awk选项:
-F 指定字段分隔符。
-f 指定awk脚本文件。
-v 定义变量。
pattern表示匹配的模式,也即文本搜索条件,可以是字符串或者正则表达式,如果匹配成功,则会执行接下来的action命令。
awk命令中action命令非常灵活,可以是任何有效的awk语句,例如:
print 打印指定内容。
printf 格式化输出。
if-else 条件语句。
while 循环语句。
getline 读取下一行输入。
三 awk变量
AWK变量有以下几种:
1.内置变量:内置变量是由AWK本身定义的变量,例如FS,RS等。
2.用户自定义变量:用户自定义变量是由用户自己定义的变量,可以用来存储数据、计算结果等。
3.环境变量:环境变量是由操作系统提供的变量,可以被AWK程序访问和使用。
4.命令行参数变量:命令行参数变量是在执行AWK程序时通过命令行传递的变量,一般用来指定文件名、分隔符等参数。
5.数组变量:数组变量是AWK中一种特殊的变量类型,可以用来存储多个值,可以通过下标来访问数组中的元素。
常用的是内置变量:
- $0:表示整个记录。
- $1:表示第一个字段。(即第一列)
- $2:表示第二个字段。
- …:以此类推。
- NF:表示当前记录中的字段数。(列数)
- NR:表示已经读出的记录数。(行数)
- FS:表示输入字段分隔符。
- RS:表示输入记录分隔符。
- OFS:表示输出字段分隔符。
- ORS:表示输出记录分隔符。
更多:
$n : 当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
$0 : 这个变量包含执行过程中当前行的文本内容。
ARGC : 命令行参数的数目。
ARGIND : 命令行中当前文件的位置(从0开始算)。
ARGV : 包含命令行参数的数组。
CONVFMT : 数字转换格式(默认值为%.6g)。
ENVIRON : 环境变量关联数组。
ERRNO : 最后一个系统错误的描述。
FIELDWIDTHS : 字段宽度列表(用空格键分隔)。
FILENAME : 当前输入文件的名。
NR : 表示记录数,在执行过程中对应于当前的行号
FNR : 同NR :,但相对于当前文件。
FS : 字段分隔符(默认是任何空格)。
IGNORECASE : 如果为真,则进行忽略大小写的匹配。
NF : 表示字段数,在执行过程中对应于当前的字段数。 print $NF答应一行中最后一个字段
OFMT : 数字的输出格式(默认值是%.6g)。
OFS : 输出字段分隔符(默认值是一个空格)。
ORS : 输出记录分隔符(默认值是一个换行符)。
RS : 记录分隔符(默认是一个换行符)。
RSTART : 由match函数所匹配的字符串的第一个位置。
RLENGTH : 由match函数所匹配的字符串的长度。
SUBSEP : 数组下标分隔符(默认值是34)。
四、awk简单使用示例
和grep一样,awk不会修改源文件内容。
data.txt
文件内容(最左边是vim的行号,不是文件内容):
1 活度0.7mCi粒子数,D90,活度0.8mCi粒子数,D90 2 5,393.6,5,502.4 3 10,1355.5,10,1497.3 4 15,1869.7,15,2092.2 5 20,2309.6,20,2635.8 6 25,2815.4,25,3355.5 7 30,3324.7,30,4300 8 35,4094.2,35,5508.3 9 40,4940.2,40,7560.6 10 45,6097.5,45,9708 11 50,7620.1,50,12203.7 12 55,8720.4,, 13 60,10899.8,, 14 65,12453.7,,
下面使用awk对上述文件进行一些操作。
4.1 分割文本
从上述用逗号分隔的文件中提取第1列数据:
awk -F "," '{print $1}' data.txt
输出:
root@CQUPTLEI:~/Linux_test# awk -F "," '{print $1}' data.txt 活度0.7mCi粒子数 5 10 15 20 25 30 35 40 45 50 55 60 65
这里:选项是-F
,pattern为空,action是print $1
4.2 搜索并替换文本
文件中查找所有包含15这个数字的行,并将它们替换为-1:
awk '/15/ {gsub(/\<15\>/,-1)}; /-1/' data.txt
输出:
root@CQUPTLEI:~/Linux_test# awk '/15/ {gsub(/\<15\>/,-1)}; /-1/' data.txt -1,1869.7,-1,2092.2
这里,选项为空,pattern为正则表达式:/15/ {gsub(/\<15\>/,-1)}
,表示匹配含有15这个数字的行,并将所有15替换为-1.action为:/-1/,表示输出含有-1的行。gsub函数表示替换所有匹配的项,sub则只替换匹配到的第一个。
4.3 数值计算
计算文件中第一列数字的总和和平均值:
awk -F "," '{sum+=$1} END {printf "第一列的总和为:%d,平均值为:%.2f\n",sum,sum/NR}' data.txt
输出:
root@CQUPTLEI:~/Linux_test# awk -F "," '{sum+=$1} END {printf "第一列的总和为:%d,平均值为:%.2f\n",sum,sum/NR}' data.txt 第一列的总和为:455,平均值为:32.50
注意这里的NR
上面只是在命令行进行简单操作,但awk的功能远不止如此,还可以编写awk脚本,实现更加复杂的功能。(回想一下grep的规则文件,使用 `-f`参数)
五、使用awk脚本
5.1 awk脚本的基本用法
awk命令的-f参数用于执行指定的AWK脚本文件。通常,一个AWK脚本文件包含一个或多个AWK命令和一些描述性文本,也可以包含函数、条件语句等。
-f参数的语法如下:
awk -f scriptfile inputfile
其中,scriptfile是一个AWK脚本文件(.awk
),inputfile是要处理的输入文件。
这是一个awk脚本文件:rule.awk
#!/usr/bin/awk -f BEGIN {print "This is a test script."} {split($0,tmp,",");print tmp[1]} END {print"处理的行数为:", NR}
注:
第一行的#!/usr/bin/awk -f指定了该文件使用的AWK解释器和解释参数
BEGIN是一个模式,它表示在处理输入之前执行动作
同理:END是一个特殊的模式,它表示在处理完所有输入之后执行动作;
awk文件包含一个或多个awk命令。每个命令都由一个模式和一个动作组成,用大括号括起来。
也可以写成:
BEGIN {FS=","} {split($0,a,","); print a[1]}
上面的脚本将data.txt用逗号分隔,并输出第一列,执行效果:
root@CQUPTLEI:~/Linux_test# awk -f rule.awk data.txt This is a test script. 活度0.7mCi粒子数 5 10 15 20 25 30 35 40 45 50 55 60 65 处理的行数为: 14
这里仍然是一个简单的awk脚本,要编写功能更加复杂的awk脚本,还需要学习更多awk知识。
5.2 进阶知识点
awk还是一种编程语言,前面已经介绍过awk变量,本节将接收awk函数、运算、语句等。
5.21 awk语句
(1) for循环
for(变量 in 数组) {语句} for(变量;条件;表达式) {语句}
(2)while循环
while(表达式) {语句}
(3) do…while循环
do {语句} while(条件)
其他相关语句
break
:退出程序循环continue
: 进入下一次循环next
:读取下一个输入行exit
:退出主输入循环,进入END,若没有END或END中有exit语句,则退出脚本。
5.22 awk运算
- 算术运算:(+,-,*,/,&,!,……,++,–)
所有用作算术运算符进行操作时,操作数自动转为数值,所有非数值都变为0
- 赋值运算:(=, +=, -=,*=,/=,%=,……=,**=)
- 逻辑运算符: (||, &&)
- 关系运算符:(<, <=, >,>=,!=, ==)
- 正则运算符:(~,~!)(匹配正则表达式,与不匹配正则表达式)
awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}' ok
5.23 awk高级输入输出
读取下一条记录:next 语句
awk中next语句使用:在循环逐行匹配,如果遇到next,就会跳过当前行,直接忽略下面语句。而进行下一行匹配。next语句一般用于多行合并:
root@CQUPTLEI:~/Linux_test# awk 'NR%2==1{next}{print NR,$0;}' data.txt 2 5,393.6,5,502.4 4 15,1869.7,15,2092.2 6 25,2815.4,25,3355.5 8 35,4094.2,35,5508.3 10 45,6097.5,45,9708 12 55,8720.4,, 14 65,12453.7,,
说明: 跳过奇数行。下面的print NR,$0
也不会执行。下一行开始,程序继续判断NR%2
值。这个时候记录行号是偶数 ,就会执行下面语句块:`print NR,$0
读取一行记录:getline 语句
awk getline用法:输出重定向需用到getline函数。getline从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给NF,NR和FNR等内建变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,如果出现错误,例如打开文件失败,就返回-1。
语法格式:getline var 变量var包含了特定行的内容
用法说明:
当其左右无重定向符时|,<时:getline作用于当前文件,读入当前文件的第一行给其后跟的变量var或$0(无变量),应该注意到,由于awk在处理getline之前已经读入了一行,所以getline得到的返回结果是隔行的。
当其左右有重定向符时|,<时:getline则作用于定向输入文件,由于该文件是刚打开,并没有被awk读入一行,只是getline读入,那么getline返回的是该文件的第一行,而不是隔行。
5.24 awk函数
(1)算术函数
格式 | 描述 |
atan2( y, x ) | 返回 y/x 的反正切。 |
cos( x ) | 返回 x 的余弦;x 是弧度。 |
sin( x ) | 返回 x 的正弦;x 是弧度。 |
exp( x ) | 返回 x 幂函数。 |
log( x ) | 返回 x 的自然对数。 |
sqrt( x ) | 返回 x 平方根。 |
int( x ) | 返回 x 的截断至整数的值。 |
rand( ) | 返回任意数字 n,其中 0 <= n < 1。 |
srand( [expr] ) | 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。 |
(2)字符串函数
格式 | 描述 |
gsub( Ere, Repl, [ In ] ) | 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。 |
sub( Ere, Repl, [ In ] ) | 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。 |
index( String1, String2 ) | 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。 |
length [(String)] | 返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
blength [(String)] | 返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
substr( String, M, [ N ] ) | 返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。 |
match( String, Ere ) | 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。 |
tolower( String ) | 返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 |
toupper( String ) | 返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 |
sprintf(Format, Expr, Expr, . . . ) | 根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。 |
说明: Ere都可以是正则表达式。
(3)时间函数
格式 | 描述 |
mktime( YYYY MM dd HH MM ss[ DST]) | 生成时间格式 |
strftime([format [, timestamp]]) | 格式化时间输出,将时间戳转为时间字符串 具体格式,见下表. |
systime() | 得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数 |
5.3 复杂awk脚本示例
还是前文的data.txt文件:写一个awk脚本,将第一列数据中小于30的数据替换文这一列的平均值;将第二列中的数字的小数部分去掉,将第3、4列中缺失的数据用0补全。最后输出修改后的文件以及文件的行数和列数
rule2.awk:
#!/bin/awk -f BEGIN { FS=","; OFS=FS; avg1=0; avg2=0; avg3=0; count=0; } NR==1 { print; } NR>1 { if ($1<30) { sum1+=30; count1++; $1=30; } else { sum1+=$1; count1++; } $2=sprintf("%.0f",$2); if ($2=="") { $2=0; } if ($3=="") { $3=0; count2++; } else { sum2+=$3; count2++; } if ($4=="") { $4=0; count3++; } else { sum3+=$4; count3++; } print; } END { avg1=sum1/count1; avg2=sum2/count2; avg3=sum3/count3; print "行数:" NR; print "列数:" NF; }
运行:
root@CQUPTLEI:~/Linux_test# awk -f rule2.awk data.txt 活度0.7mCi粒子数,D90,活度0.8mCi粒子数,D90 30,394,5,502.4 30,1356,10,1497.3 30,1870,15,2092.2 30,2310,20,2635.8 30,2815,25,3355.5 30,3325,30,4300 35,4094,35,5508.3 40,4940,40,7560.6 45,6098,45,9708 50,7620,50,12203.7 55,8720,0,0 60,10900,0,0 65,12454,0,0 行数:14 列数:4
六、为什么使用awk
在不知道或者不掌握awk时,我们可能经常使用python(或者office、matlab等)进行文本、数据处理。
拿awk与Python做一个比较:
awk和Python都是流行的脚本语言,它们都有自己的优缺点。
awk的优点包括:
- awk是一种非常快速和高效的文本处理工具,它可以轻松地处理大型文本文件。
- awk具有强大的文本处理功能,包括模式匹配、正则表达式、字段分隔符等。
- awk是一种非常灵活的语言,可以轻松地编写简单的脚本来完成各种任务。
awk的缺点包括:
awk不是一种通用编程语言,它主要用于文本处理和数据转换。
awk的语法可能比其他编程语言更难学习和理解。
awk不支持面向对象编程。
Python的优点包括:
Python是一种通用编程语言,可以用于各种任务,包括Web开发、数据分析、机器学习等。
Python具有简单易学的语法,使得它成为初学者入门编程的理想选择。
Python有一个庞大的社区和生态系统,提供了许多有用的库和工具。
Python的缺点包括:
Python可能比其他编程语言更慢,因为它是解释性语言。
Python可能不如其他编程语言那么适合处理大型数据集。
Python可能不如其他编程语言那么适合高性能计算。
很显然,awk能完成的工作,Python完全能够胜任,但在某些场合,写个awk或许更加简单、快捷(前提是你掌握awk)。
选择哪种工具取决于您要完成的任务以及您对这些工具的熟悉程度。