如何编写一个优雅的Shell脚本(三)

简介

awk是shell脚本里面文本处理神奇,它虽然没有像Java、C、C++这些高级语言那样开发服务类型的应用程序,但是它擅长的是处理文件。本博客主要是对自己以往工作中使用awk的一个总结,不会对awk的语法做一个全面介绍,如果像更深入的了解AWK,推荐大家一本awk的书籍《awk与sed》。但是会对工作用到的awk一些功能进行介绍。废话不少说,进入正题吧。

awk语法

awk其实就是将文件一行一行的遍历处理,每一行代码处理文本中的一行记录,读取的顺序。

  • 按照文件的参数传入顺序进行读取
  • 每一个文件都是从第一行到最后以后顺序读取

一个AWK程序的基本组成部分,包括如下

  • BEGIN: 相当于Java中类的构造函数,程序启动第一个运行的块
  • 主体部分:主体部分通过两个中括号括起来,一个awk程序里面可以有多个中括号,多个中括号括起来会重复便利参数文件里面的内容进行处理(这种功能一半用的比较少)。
  awk [option] 'BEGIN{
  }
  [partition]{
  }END{
  }' 参数文件列表
awk -f awk脚本文件 参数文件列表

option ,项目中使用较多的选项有 -f -F -v。
-f选项是将awk的程序写在一个文件里面,通过-f参数将文件里面的内容当作awk脚本程序。

$ cat test.data
1,hello,awk
2,hello,shell
$ cat test.awk
BEGIN{ print "begin" }
{
  print $0
}
END { print "end" }
$ awk -f test.awk test.data 
begin
1,hello,awk
2,hello,shell
end

-F 制定解析文件的分隔符,可以多个字符。默认分隔符为空格和制表符。也可以用FS内置变量来实现。

$ awk -F"," '{print $2}' test.data
hello
hello

-v,可以将shell脚本里面的变量传送到awk中。

$ shellVar="awk"
$ awk -F"," -v shellVar=$shellVar '{print shellVar}' test.data
awk
awk
$ awk -F"," -v shellVar=$shellVar '$3==shellVar{print $0}' test.data
1,hello,awk

awk内置变量

变量 含义
ARGC 命令行参数个数
ARGV 命令行参数数组
FILENAME 当前输入文件名
FNR 当前文件中的记录号
FS 输入域分隔符,默认为一个空格
RS 输入记录分隔符
NF 当前记录里域个数
NR 到目前为止记录数
OFS 输出域分隔符
ORS 输出记录分隔符

awk内置函数

  • split( String, A, [Ere] )
    将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。
 $ awk '{
    split($0,a,",");
    print a[1],a[2],a[3]
 }' test.data
1 hello awk
2 hello shell

# 以上输出数组a,split返回的数组下标以1开始。我们也可以通过OFS变量制定输出分隔符
$ awk 'BEGIN {OFS="~"}{
    split($0,a,",");
    print a[1],a[2],a[3]
 }' test.data
1~hello~awk
2~hello~shell
  • gsub( Ere, Repl, [ In ] )
    除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。

  • sub( Ere, Repl, [ In ] )
    用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。

 $  awk -F"," '{a=$3; gsub("awk","我是被替换的", $3); print a, $3}' test.data
awk 我是被替换的
shell shell
  • match( String, Ere )
    在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。
  $  awk -F"," '{match($3,'/^[a-z]{2}k$/'); print RSTART, RLENGTH}' test.data  
1 3
0 -1
# 可以用这个函数,通过正则表达式去匹配字符串里面的子字符串
$ awk '{
value=$1
while(match(value,/\w+,/) > 0) {
  print substr(value,RSTART,RLENGTH-1);
  value=substr(value,RLENGTH+1);
} print value
}' test.data
1
hello
awk
2
hello
shell
  • tolower( String )
    返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
  • toupper( String )
    返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。

awk实践

awk文件关联

awk可以像SQL一样,实现两个和多个文件关联。
有如下两个文件

$ cat test2.data 
1,测试关联
$ cat test.data  
1,hello,awk
2,hello,shell

# test.data 左关联test2.data
$ awk -F"," '{if (FILENAME=="test2.data") { a[$1]=$2 } else {
  print $0, a[$1]
}}' test2.data test.data
1,hello,awk 测试关联
2,hello,shell

awk文件拆分

现实工作中,有这样一个需求,一个接口文件中,每行记录有1个或多个字段包含了一个数组,我们需要将数组进行拆分成一条条的记录。

$ cat test3.data
1,test1,数组11&数组12
2,test2,数组21&数组22
# 拆分后的结果
$ awk -F"," '{
split($3,a,"&");  # 将第三个字段进行拆分到a数组
for (i in a) {
  print $1,$2,a[i]
}
}' test3.data
1 test1 数组11
1 test1 数组12
2 test2 数组21
2 test2 数组22

总结

本文主要是针对过往一些工作中使用到shell脚本里面的awk命令进行总结,总结的内容属于awk的冰山一角。如果awk吸引到你,你可以去系统的了解以下awk。建议看看《awk与sed》这本书,或者再linux系统中使用 man命令获取awk的帮助文档。

猜你喜欢

转载自blog.csdn.net/IT_xhf/article/details/83900788