和散仙学shell编程(十二)
跟散仙学shell编程(十二)
上篇文章散仙写了关于sed进阶,本篇我们来看下在linux中另外一种非常强大的文本处理语言gawk,有关于gawk的基础,散仙在前2篇文章,也有介绍,不熟悉的朋友,可以再回顾下。
gawk是一门功能丰富的编程语言,允许你通过编写高级程序来处理数据,只要我们有其他编程语言的经验,不管死JAVA,C#,Python,shell还是javascript,入手gawk都会感到非常亲切和容易。
下面来看下gawk里面的变量,在gawk里面总共有2种不同的变量,第一种是自带变量,第二种是自定义变量,下面来看下具体的几个常用的变量:
变量FS和OFS定义了gawk如何处理数据流中的数据字段,默认情况下gawk将OFS设置成一个空格:
输出字段分隔符:
下面看FIELDWIDTHSB变量,它会按固定的位数分隔,但是对于变长的字段就不适合了
下面看行分隔符的使用:
分析下流程,上述的例子里面我们把FS的分隔符设置为换行符,这说明gawk会把整个行当做一个字段,然后我们又把行分隔符设置为空格,然后再数据行间留一个空白行,gawk会把一个空行当成一个分隔符
除了上面的分隔符变量外,gawk里面还有一些其他的变量:
ARGC 当前命令行的个数
ARGIND 当前文件再ARGV里面的位置
ARGV 包含命令行参数的数组
FNR 当前数据文件中的数据行数
NF 数据文件里字段总数
ARG变量表明命令行有两个参数,这包括gawk命令和cc参数,第一个数组值是gawk命令后的第一个命令行参数
ENVIRON命令可以从SHELL的变量里,提取值,但它使用的是字符串,不是索引来标识。
NF变量允许你指定数据行里最后一个字段,而不需要知道到底有多少个字段
FNR变量含有当前处理过数据行的总数,NR变量含有所有的总数
下面看下如何在gawk里面,使用自定义变量:
已经使用的变量可以再次使用。
也可以在gawk里面使用算术表达式
还可以在命令端输出参数:
如果是在BEGIN前定义的变量,可能不会解析出来,我们需要加上-v参数
在gawk里面我们也可以很方便的使用数组变量:
遍历数组也可以用我们最熟悉的foreach的方式:
删除一个数组变量用delete命令:
下面如何使用正则表达式结合gawk
下面看下匹配操作符:
$n ~ 可以指定在某个列里面进行查询过滤
!号可以排序,正则的过滤:
数学表达式:
也可以比较文本:
下面看下在gawk里面的结构化编程:
下面看下 if-else的例子:
下面看下while语句:
gawk支持在while里使用break和continue关键字:
下面看下for语句:
使用起来,非常顺手
gawk还支持格式化输出printf命令
gawk还支持一些常用的内置函数:
cos(x) 余弦
log(x)对数
rand() 比0大比1小的随机浮点值
除此之外,还有一些字符串函数,有过其他编程基础的人,很容易看明白下面的函数
length(s)取字符串长度
split()按某个符号拆分
asort()排序
match()匹配子串
tolower()转小写
toupper()转大写
等等
下面看下时间函数:
mktime(datespec)将一个按YYYY-MM-DD HH MM SS 格式转换时间错
strftime(format,[timestamp]) 格式化日期
systime() 返回当前的时间戳
下面看下自定义函数:
需要注意的是函数名是唯一的
最后看下,如何在gawk自定义一些函数库:
我们不能直接在shell终端上使用库函数,但是我们可以用多个-f命令执行多个文件;
上篇文章散仙写了关于sed进阶,本篇我们来看下在linux中另外一种非常强大的文本处理语言gawk,有关于gawk的基础,散仙在前2篇文章,也有介绍,不熟悉的朋友,可以再回顾下。
gawk是一门功能丰富的编程语言,允许你通过编写高级程序来处理数据,只要我们有其他编程语言的经验,不管死JAVA,C#,Python,shell还是javascript,入手gawk都会感到非常亲切和容易。
下面来看下gawk里面的变量,在gawk里面总共有2种不同的变量,第一种是自带变量,第二种是自定义变量,下面来看下具体的几个常用的变量:
序号 | gawk自带变量名 | 描述 |
1 | FIELDWIDTS | 由空格分隔开的定义了每个数据字段确切宽度的一列数字 |
2 | FS | 输出字段分隔符 |
3 | RS | 输入数据的行分隔符 |
4 | OFS | 输出字段分隔符 |
5 | ORS | 输出数据行分隔符 |
变量FS和OFS定义了gawk如何处理数据流中的数据字段,默认情况下gawk将OFS设置成一个空格:
[search@h1 822]$ gawk '{print $1,$2,$3}' b.txt a b c solr hadoop lucene [search@h1 822]$ cat b.txt a b c solr hadoop lucene [search@h1 822]$ gawk '{print $1,$2,$3}' b.txt a b c solr hadoop lucene [search@h1 822]$ gawk '{print $1,$2}' b.txt a b solr hadoop [search@h1 822]$
输出字段分隔符:
[search@h1 822]$ cat b.txt a b c solr hadoop lucene [search@h1 822]$ gawk 'BEGIN{FS=" ";OFS="-"} {print $1,$2,$3 }' b.txt a-b-c solr-hadoop-lucene [search@h1 822]$ gawk 'BEGIN{FS=" ";OFS="--"} {print $1,$2,$3 }' b.txt a--b--c solr--hadoop--lucene [search@h1 822]$ gawk 'BEGIN{FS=" ";OFS="-->"} {print $1,$2,$3 }' b.txt a-->b-->c solr-->hadoop-->lucene [search@h1 822]$
下面看FIELDWIDTHSB变量,它会按固定的位数分隔,但是对于变长的字段就不适合了
[search@h1 822]$ cat c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ gawk 'BEGIN{FIELDWIDTHS="2 3 2"}{print $1,$2,$3}' c.txt 10 034 55 12 345 22 22 222 22 56 788 90 [search@h1 822]$
下面看行分隔符的使用:
[search@h1 822]$ cat cc 中国 河南 洛阳1 电话 2522252 中国 河南 洛阳2 电话 2522252 中国 河南 洛阳3 电话 2522252 [search@h1 822]$ gawk 'BEGIN{FS="\n"; RS=""} {print $1,$2}' cc 中国 河南 洛阳1 电话 2522252 中国 河南 洛阳2 电话 2522252 中国 河南 洛阳3 电话 2522252 [search@h1 822]$
分析下流程,上述的例子里面我们把FS的分隔符设置为换行符,这说明gawk会把整个行当做一个字段,然后我们又把行分隔符设置为空格,然后再数据行间留一个空白行,gawk会把一个空行当成一个分隔符
除了上面的分隔符变量外,gawk里面还有一些其他的变量:
ARGC 当前命令行的个数
ARGIND 当前文件再ARGV里面的位置
ARGV 包含命令行参数的数组
FNR 当前数据文件中的数据行数
NF 数据文件里字段总数
[search@h1 822]$ gawk 'BEGIN{print ARGC,ARGV[1]}' cc 2 cc [search@h1 822]$
ARG变量表明命令行有两个参数,这包括gawk命令和cc参数,第一个数组值是gawk命令后的第一个命令行参数
[search@h1 822]$ gawk 'BEGIN{print ENVIRON["HOME"] ; print ENVIRON["PATH"] } ' /home/search .:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/jdk/bin:/home/search/hadoop/bin:/home/search/hadoop/sbin:/usr/local/ant/bin:/usr/local/maven/bin:/home/search/hive/bin:/home/search/hive/conf:/home/search/bin [search@h1 822]$
ENVIRON命令可以从SHELL的变量里,提取值,但它使用的是字符串,不是索引来标识。
[search@h1 822]$ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd root:/bin/bash bin:/sbin/nologin daemon:/sbin/nologin adm:/sbin/nologin lp:/sbin/nologin sync:/bin/sync shutdown:/sbin/shutdown halt:/sbin/halt mail:/sbin/nologin uucp:/sbin/nologin operator:/sbin/nologin games:/sbin/nologin gopher:/sbin/nologin ftp:/sbin/nologin nobody:/sbin/nologin vcsa:/sbin/nologin saslauth:/sbin/nologin postfix:/sbin/nologin sshd:/sbin/nologin mysql:/bin/bash search:/bin/bash [search@h1 822]$
NF变量允许你指定数据行里最后一个字段,而不需要知道到底有多少个字段
[search@h1 822]$ cat c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ gawk 'BEGIN{FS=","} {print $1, "FNR="FNR}' c.txt 1003455 FNR=1 1234522 FNR=2 2222222 FNR=3 5678890 FNR=4 [search@h1 822]$
FNR变量含有当前处理过数据行的总数,NR变量含有所有的总数
[search@h1 822]$ cat c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ gawk 'BEGIN{FS=","} {print $1, "FNR="FNR}' c.txt 1003455 FNR=1 1234522 FNR=2 2222222 FNR=3 5678890 FNR=4 [search@h1 822]$ gawk 'BEGIN{FS=","} {print $1, "FNR="FNR, "NR="NR}' c.txt 1003455 FNR=1 NR=1 1234522 FNR=2 NR=2 2222222 FNR=3 NR=3 5678890 FNR=4 NR=4 [search@h1 822]$ gawk 'BEGIN{FS=","} {print $1, "FNR="FNR, "NR="NR}' c.txt c.txt 1003455 FNR=1 NR=1 1234522 FNR=2 NR=2 2222222 FNR=3 NR=3 5678890 FNR=4 NR=4 1003455 FNR=1 NR=5 1234522 FNR=2 NR=6 2222222 FNR=3 NR=7 5678890 FNR=4 NR=8 [search@h1 822]$
下面看下如何在gawk里面,使用自定义变量:
[search@h1 822]$ gawk 'BEGIN{test="你好,哈喽"; print test}' 你好,哈喽 [search@h1 822]$
[search@h1 822]$ gawk 'BEGIN{test="你好,哈喽"; print test ; test=123; print test}' 你好,哈喽 123 [search@h1 822]$
已经使用的变量可以再次使用。
也可以在gawk里面使用算术表达式
[search@h1 822]$ gawk 'BEGIN{x=4; x=x * 2 + 3 ; print x}' 11 [search@h1 822]$
还可以在命令端输出参数:
[search@h1 822]$ cat c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ gawk -f script n=1 c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ cat script BEGIN{FS=","} {print $n} [search@h1 822]$
[search@h1 822]$ gawk -f s2 n=1 c.txt 这个数值: 1003455 1234522 2222222 5678890 [search@h1 822]$ cat c.txt 1003455 1234522 2222222 5678890 [search@h1 822]$ cat s2 BEGIN{ print "这个数值: "; n; FS="."} { print $n } [search@h1 822]$
如果是在BEGIN前定义的变量,可能不会解析出来,我们需要加上-v参数
在gawk里面我们也可以很方便的使用数组变量:
[search@h1 822]$ gawk 'BEGIN{ a["key"]="value" ; print a["key"]}' value [search@h1 822]$
遍历数组也可以用我们最熟悉的foreach的方式:
[search@h1 822]$ gawk 'BEGIN{ a["1"]="aa"; a["2"]="ab" ; a["3"]="ac" ; for (t in a){ print "索引: ",t," 值: ", a[t] } }' 索引: 1 值: aa 索引: 2 值: ab 索引: 3 值: ac [search@h1 822]$
删除一个数组变量用delete命令:
[search@h1 822]$ gawk 'BEGIN{ a["1"]="aa"; a["2"]="ab" ; a["3"]="ac" ; delete a["2"] ; for (t in a){ print "索引: ",t," 值: ", a[t] } }' 索引: 1 值: aa 索引: 3 值: ac [search@h1 822]$
下面如何使用正则表达式结合gawk
[search@h1 822]$ cat a.txt data11 data22 data111 data4561 [search@h1 822]$ gawk 'BEGIN{FS=","} /11/{print $1} ' a.txt data11 data111 [search@h1 822]$
下面看下匹配操作符:
[search@h1 822]$ cat a.txt data11 test11 bbb111 data22 kkk222 ccc222 data111 ggg jjjj data4561 111 3333 [search@h1 822]$ gawk 'BEGIN{FS=" "} $2 ~ /^kk/{ print $0 }' a.txt data22 kkk222 ccc222 [search@h1 822]$ gawk 'BEGIN{FS=" "} $1 ~ /1/{ print $0 }' a.txt data11 test11 bbb111 data111 ggg jjjj data4561 111 3333 [search@h1 822]$ gawk 'BEGIN{FS=" "} $1 ~ /^1/{ print $0 }' a.txt [search@h1 822]$ gawk 'BEGIN{FS=" "} $1 ~ /^11/{ print $0 }' a.txt [search@h1 822]$ gawk 'BEGIN{FS=" "} $1 ~ /11/{ print $0 }' a.txt data11 test11 bbb111 data111 ggg jjjj [search@h1 822]$
$n ~ 可以指定在某个列里面进行查询过滤
!号可以排序,正则的过滤:
[search@h1 822]$ cat a.txt data11 test11 bbb111 data22 kkk222 ccc222 data111 ggg jjjj data4561 111 3333 [search@h1 822]$ gawk 'BEGIN{FS=" "} $1 !~ /11/{ print $0 }' a.txt data22 kkk222 ccc222 data4561 111 3333 [search@h1 822]$
数学表达式:
[search@h1 822]$ cat x.txt 1 solr 2 lucne 3 hadoop 1 solr2` [search@h1 822]$ gawk '$1 == 1 { print $0 }' x.txt 1 solr 1 solr2` [search@h1 822]$
也可以比较文本:
[search@h1 822]$ cat x.txt 1 solr 2 lucne 3 hadoop 1 solr2` [search@h1 822]$ gawk '$1 == 1 { print $0 }' x.txt 1 solr 1 solr2` [search@h1 822]$ gawk '$2 == "hadoop" { print $0 }' x.txt 3 hadoop [search@h1 822]$
下面看下在gawk里面的结构化编程:
[search@h1 822]$ cat d1 10 100 20 45 1 -11 [search@h1 822]$ gawk '{ if ( $1 > 10 ) print $1} ' d1 100 20 45 [search@h1 822]$
下面看下 if-else的例子:
[search@h1 822]$ cat d1 10 100 20 45 1 -11 [search@h1 822]$ gawk '{ if ( $1 > 10 ) print $1} ' d1 100 20 45 [search@h1 822]$ gawk '{ if ( $1 > 10 ) { print $1} else { print "这里面小于10" } } ' d1 这里面小于10 100 20 45 这里面小于10 这里面小于10 [search@h1 822]$
下面看下while语句:
[search@h1 822]$ gawk 'BEGIN{ a=4; while (a<10){ a++ ; print a} }' 5 6 7 8 9 10 [search@h1 822]$
gawk支持在while里使用break和continue关键字:
[search@h1 822]$ gawk 'BEGIN{ a=4; while (a<10){ a++ ; if(a==9){ break ;} if (a==7){ continue;} print a} }' 5 6 8 [search@h1 822]$
下面看下for语句:
[search@h1 822]$ gawk 'BEGIN{ for(i=1;i<=10;i++) { print "当前的值:"i }}' 当前的值:1 当前的值:2 当前的值:3 当前的值:4 当前的值:5 当前的值:6 当前的值:7 当前的值:8 当前的值:9 当前的值:10 [search@h1 822]$
使用起来,非常顺手
gawk还支持格式化输出printf命令
gawk还支持一些常用的内置函数:
cos(x) 余弦
log(x)对数
rand() 比0大比1小的随机浮点值
[search@h1 822]$ gawk 'BEGIN{ x=10 * rand() ; print x}' 2.37788
除此之外,还有一些字符串函数,有过其他编程基础的人,很容易看明白下面的函数
length(s)取字符串长度
split()按某个符号拆分
asort()排序
match()匹配子串
tolower()转小写
toupper()转大写
等等
下面看下时间函数:
mktime(datespec)将一个按YYYY-MM-DD HH MM SS 格式转换时间错
strftime(format,[timestamp]) 格式化日期
systime() 返回当前的时间戳
[search@h1 822]$ gawk 'BEGIN{ print systime()}' 1408741666 [search@h1 822]$
下面看下自定义函数:
[search@h1 822]$ gawk ' function m(){ print "我是自定义的方法" } BEGIN{ m(); }' 我是自定义的方法 [search@h1 822]$
需要注意的是函数名是唯一的
最后看下,如何在gawk自定义一些函数库:
[search@h1 822]$ cat ak function a(){ print "我是自定义的库" } function b(i){ for( b=i;b<10;b++){ print "我是自定义的第二个方法: "b } } function c(){ print rand() } [search@h1 822]$ cat ax BEGIN{ a() } [search@h1 822]$ cat axx BEGIN{ c(); } [search@h1 822]$ gawk -f ak -f ax 我是自定义的库 [search@h1 822]$ gawk -f ak -f axx 0.237788 [search@h1 822]$
我们不能直接在shell终端上使用库函数,但是我们可以用多个-f命令执行多个文件;