Linux学习笔记(七)

mark

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
Researcher

I am a PhD student of Crop Genetics and Breeding at the Zhejiang University Crop Science Lab. My research interests covers a range of issues:Population Genetics Evolution and Ecotype Divergence Analysis of Oilseed Rape, Genome-wide Association Study (GWAS) of Agronomic Traits.

comments powered by Disqus