Linux学习笔记(七)
awk命令
awk是一种编程语言,用于在unix下处理数据以及文本。数据可以来自stdin、一个或者多个文件或者其它命令的输出,同时支持自定义函数和正则表达式,更多是作为脚本来使用。
awk命令格式以及选项
awk使用一个或多个如下结构:
pattren { action }
每一个pattern是一个表达式或者正则表达式,pattern类似于其它编程语言中的if语句:如果表达式为TRUE或者正则表达式匹配到的话,后面的action命令会被执行。
awk语法形式
awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)
常用命令选项:
- -F fs:fs指定输入分隔符,fs可以是字符串或者正则表达式
- -v var=value:赋值一个用户定义变量,将外部变量传递给awk
- -f scriptfile:从脚本文件中读取awk命令
- -m[fr] val:对val值设置内在限制,-mf选项限制分配给val的最大块数目;-mr选项限制记录的最大块数目,这两个功能是扩展功能,在标准awk中不适用。
awk模式以及操作
上面已经讲了pattern以及action,awk就是有一个或多个模式与操作组成的。
模式pattern
pattern可以是以下任意一种:
- 正则表达式
- 关系表达式:使用运算符操作,可以是字符串或者数字的比较测试
- 模式匹配表达式:用运算符
~
(匹配)和!
(不匹配) - BEGIN语句块、pattern语句块以及END语句块,涉及十分复杂,后续再讲解
操作
操作由一个或多个命令、函数表达式组成,相互之间使用换行符或者分号分开,并位于大括号内。主要部分是:
- 变量或者数组赋值
- 输出命令
- 内置函数
- 控制流语句
awk脚本基本结构
awk 'BEGIN{ print "start"} pattern{ command } END{ print "end"}' file
一个awk脚本通常是由:BEIGIN语句块、能够使用模式匹配的通用语句块、END语句块三部分组成,这三部分是可选的,任意一部分都可以不出现在脚本中。脚本通常是在'‘或"“中:
awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename
awk "BEGIN{ i=0 } { i++ } END{ print i }" filename
awk的工作原理
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
工作原理:
- 第一步是执行
BEGIN{ commands }
语句块中的语句 - 第二步是从文件或者标准输入中读取一行,然后执行
pattern{ commands }
语句块,它是逐行扫描文件,从第一行到最后一行重复此命令直到文件被完全读取完毕 - 最后当读至输入流末尾时,执行
END{ commands }
语句块
注意事项:
BEGIN{ commands }
语句块在awk开始从输入流中读取之前被执行,是一个可选语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在此语句块中END{ commands }
语句块在awk从输入流中读取所有行之后被执行,比如打印所有行的分析结果这类信息汇总都是在此语句块中完成,也是一个可选语句块pattern{ commands }
语句块是最重要的组成部分,也是可选的,如果没有提供此语句块,则默认执行{ print },即打印每一个读取到的行,awk读取到的每一行都会执行该语句块
我们举个例子来说明一下:打印出example.bed文件的每一行,并且在开始读取之前打印出start,读取完毕之后打印出end
$ awk 'BEGIN{print "start"}{print}END{print "end"}' example.bed
start
chr1 26 39
chr1 32 47
chr3 11 28
chr1 40 49
chr3 16 27
chr1 9 28
chr2 35 54
chr1 10 19
end
敲黑板了!!!当print不带任何参数时,它就打印当前行,当print的参数以逗号进行分隔时,打印时则以空格作为定界符,在awk的print语句块中双引号是被当作拼接符来使用的。
$ echo|awk '{var1="v1";var2="v2";var3="v3";print var1,var2,var3;}'
v1 v2 v3
$ echo|awk '{var1="v1";var2="v2";var3="v3";print var1"="var2"="var3;}'
v1=v2=v3
{}类似于循环体,会对文件每一行进行迭代,通常变量初始化语句(比如i=0)以及打印文件头部的语句放在BEGIN语句块中,而将打印的结果等语句放在END语句块中。
awk内置变量
awk内置大量的变量方便我们直接调用
- $n:当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二字段,以此类推
- $0:表示执行过程中当前行的文本内容
- FS:字段分隔符,默认是空格
- IGNORECASE:如果为真表示忽略大小写进行匹配
- NF:表示字段数,在执行过程中对应于当前的字段数
- NR:表示记录数,在执行过程中对应于当前的行号
- OFMT:数字的输出格式,默认为%.6g
- OFS:输出字段分隔符,默认值为空格
- ORS:输出记录分隔符,默认值为一个换行符
- RS:记录分隔符:默认是空格
- RSTART:由match函数所匹配的字符串的第一个位置
- RLENGTH:由match函数所匹配到的字符串的长度
举例说明部分变量:
$ head -n 3 example.bed|awk '{print "Line No:"NR", No of fields:"NF, "$0="$0,"$1="$1,"$2="$2,"$3="$3}'
Line No:1, No of fields:3 $0=chr1 26 39 $1=chr1 $2=26 $3=39
Line No:2, No of fields:3 $0=chr1 32 47 $1=chr1 $2=32 $3=47
Line No:3, No of fields:3 $0=chr3 11 28 $1=chr3 $2=11 $3=28
上面我们先选取前三行,然后分别打印出每一行的行号,每一行的字段数以及每一行的每一个字段。
由于NF表示字段数,因此print $NF
就会打印出每一行的最后一个字段,print $(NF-1)
就会打印出倒数第二个字段,依此类推。
$ head -n 3 example.bed|awk '{print $NF}'
39
47
28
统计一个文件有多少行可以使用以下命令语句:
$ awk 'END{print NR}' Mus_musculus.GRCm38.75_chr1.gtf
81231
上面我们只是用了END语句块,在读入每一行时awk会将NR更新为对应的行号,所以当读取到最后一行时就会打印出最后一行的行号NR也就是文件的总行数了。
传递外部变量给awk
借助-v选项可以将外部值(非来自stdin)传递给awk:
$ var=1000
$ echo |awk '{print var}' var="$var"
1000
awk运算与判断
awk不仅支持算术运算还支持逻辑运算
算术运算符
运算符 | 描述 |
---|---|
+- | 加减 |
* / & | 乘 除 求余 |
+-! | 一元加减 逻辑非 |
^*** | 求幂 |
—++ – | 增加或减少,作为前缀 |
所有算术运算符进行操作,操作数自动转为数值,所有非数值转为0
赋值运算符
主要赋值运算符有:= += -= *= /= ……= **=
举例说明:
a+=5,等价于a=a+5.其它类似
逻辑运算符
就是我们常见的"或”、“与”、“非”
$ awk 'BEGIN{a=1;b=2;print (a>5&&b>2),(a>5||b<=2);}'
0 1
正则运算符
主要有~和~!即匹配正则表达式和不匹配
$ awk 'BEGIN{a="100test";if(a~/^100*/){print "OK";}}'
OK
关系运算符
主要是>、>=、=、==、!=、<、<=等
$ awk 'BEGIN{a=11;if(a>=9){print "OK";}}'
OK
其他运算符
运算符 | 描述 |
---|---|
$ | 字段引用 |
空格 | 字符串连接符 |
?: | C条件表达式 |
in | 数组中是否存在某值 |
运算符优先级
awk高级输入输出
next语句读取下一条记录
awk中的next语句使用:在循环逐行匹配,如果遇到next,就会跳过当前行,直接忽略下面的语句而进入下一匹配,一般用于多行匹配
$ awk 'NR%2==1{next}{print NR,$0;}' example.bed
2 chr1 32 47
4 chr1 40 49
6 chr1 9 28
8 chr1 10 19
后面还有很多复杂用法我暂时不讲了,再者如果遇到更复杂的情况最好使用其他更强大的工具比如python。下面我会讲讲awk如何处理生物信息学数据。
条件筛选
我们想要将长度大于18的行找出来(即end position-start position>18)
$ awk '$3-$2>18' example.bed
chr1 9 28
chr2 35 54
下面是一些常用的逻辑操作符
Comparison | Description |
---|---|
a==b | a is equal to b |
a!=b | a is not equal to b |
a < b | a is less than b |
a > b | a is greater than b |
a <= b | a is less than or equal to b |
a >= b | a is greater than or equal to b |
a ~ b | a matches regular expression pattern b |
a !~ b | a does not match regular expression pattern b |
a && b | logical and a and b |
!a | not a (logical negation) |
筛选出位于染1号染色体上长度大于10的行
$ awk '$1~/chr1/ && $3-$2>10' example.bed
chr1 26 39
chr1 32 47
chr1 9 28
$1~/chr1/
是一正则表达式匹配(正则表达式在符号斜杠里),表示第一列中能匹配到chr1的行,~
表示匹配,不匹配的话使用!~
$ awk '$1!~/chr1/ && $3-$2>10' example.bed
chr3 11 28
chr3 16 27
chr2 35 54
正则表达式结合其他复杂命令(action)可以解决很多复杂问题,比如我们想要将2和3号染色体每行长度都打印出来自成一列:
$ awk '$1!~/chr1/ {print $0 "\t" $3-$2}' example.bed
chr3 11 28 17
chr3 16 27 11
chr2 35 54 19
目前我们已经讲了awk可以使用的两个功能:
- 结合正则表达式以及算术对数据集进行过滤
- 利用算术重铸数据集的列
BEGIN和END
前面我们已经讲了BEGIN和END,BEGIN在初始化变量方面十分有用,比如如果我们想要求平均长度的话,首先我们可以求和再除以总行数
$ awk 'BEGIN{sum=0};{sum+=($3-$2)};END{print "mean: "sum/NR};' example.bed
mean: 14
由于NR表示当前行数,因此我们就可以利用它来提取数据集中的数据,比如提取example.bed中的3-5行
$ awk 'NR>=3&&NR<=5' example.bed
chr3 11 28
chr1 40 49
chr3 16 27
使用awk我们可以很方便的实现BED与GTF文件之间的转换,比如我们可以十分快速的实现从GTF文件中提取出BED文件:
$ awk '!/^#/ {print $1 "\t" $4-1 "\t" $5}' Mus_musculus.GRCm38.75_chr1.gtf|head -n 3
1 3054232 3054733
1 3054232 3054733
1 3054232 3054733
这里需要注意的是BED文件索引是从0开始的,GTF索引是从1开始的,因此start position我们需要减去1.
awk内置了很多函数可以调用:
function | description |
---|---|
length(s) | length 0f a string s |
tolower(s) | convert string s to lowercase |
touppper(s) | convert string s to uppercase |
substr(s,i,j) | return the substring of s that starts at i and ends at j |
split(s,x,d) | split string s into chunks by delimiter d,place chunks in array x |
sub(f,r,s) | find regular expression f in s and replace it with r(modifing s in place);use gsub for global substitution;returns a positive value if string is found |