站点工具
本站推荐
最多访问
- genetics(28277)
- cell(28065)
- r(25212)
- coi(21080)
- r_basics(20003)
- statistical_test(15505)
- recurrence(15174)
- lessons(14853)
- cor(14475)
- inbreeding_risk(14386)
AWK得名于三位创始人Alfred V. Aho、Peter J. Weinberger 和 Brian W. Kernighan姓的首字母,有AWK、NAWK (new AWK)、GAWK (GNU AWK)三个版本,我们这里用的是GAWK。
AWK的编程模式很简单:找到一个模式然后执行一个动作(find a pattern in the input and then perform an action)。使用AWK可以很方便地对文本的行列进行处理并格式化输出,AWK还能进行数字运算。AWK将文件分成一个个的记录(Records),又把记录拆分成一个个的字段(Fields),默认是一行一个记录,一个单词一个字段,可以更改分隔符自定义记录和字段。
这里并没有展示示例代码的输出,需要学习者运行代码。 运行代码并理解为何会得到相应结果。 给出的示例代码都是可以直接在Linux Shell运行的。
在linux系统下,使用man awk命令即能看到使用手册:
GAWK(1) Utility Commands GAWK(1)
NAME
gawk - pattern scanning and processing language
SYNOPSIS
gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
gawk [ POSIX or GNU style options ] [ -- ] program-text file ...
pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
pgawk [ POSIX or GNU style options ] [ -- ] program-text file ...
dgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
DESCRIPTION
Gawk is the GNU Project's implementation of the AWK programming language. It conforms
to the definition of the language in the POSIX 1003.1 Standard. This version in turn
is based on the description in The AWK Programming Language, by Aho, Kernighan, and
Weinberger. Gawk provides the additional features found in the current version of UNIX
awk and a number of GNU-specific extensions.
The command line consists of options to gawk itself, the AWK program text (if not sup‐
plied via the -f or --file options), and values to be made available in the ARGC and
ARGV pre-defined AWK variables.
Pgawk is the profiling version of gawk. It is identical in every way to gawk, except
that programs run more slowly, and it automatically produces an execution profile in
the file awkprof.out when done. See the --profile option, below.
Dgawk is an awk debugger. Instead of running the program directly, it loads the AWK
source code and then prompts for debugging commands. Unlike gawk and pgawk, dgawk only
processes AWK program source provided with the -f option. The debugger is documented
in GAWK: Effective AWK Programming.
这里可以看到pgawk、dgawk,另外还有个igawk,可以使用man igawk查看。用ls -l /bin/*awk可以看到相应的文件:
lrwxrwxrwx 1 root root 4 Apr 2 2018 /bin/awk -> gawk -rwxr-xr-x 1 root root 514168 Jun 29 2017 /bin/dgawk -rwxr-xr-x 1 root root 428584 Jun 29 2017 /bin/gawk -rwxr-xr-x 1 root root 3188 Jun 29 2017 /bin/igawk -rwxr-xr-x 1 root root 428672 Jun 29 2017 /bin/pgawk
awk是gawk的软连接,直接使用awk就等同于使用gawk,比如试试:
awk 'BEGIN { print "OK" }'
可以先看看这些简单的运用示例,对AWK有个初步的印象。
这里以创建一个示例文件:
echo -e "Chr\tNum\nA\t65\n\t\n\t\nB\t66\n\n\nC\t67\n \t \n \t \nX\t88\n \n \nc\t99\nC\t67\nC\t67\nc\t99" > test.txt
第3、4行只有一个制表符,第6、7行为空行,第9、10行为一个制表符分隔两个空格,第12、13行各为三个空格。
只设置pattern的时候默认print $0
awk '1' test.txt
print默认print $0,有action而不设置pattern的时候默认pattern匹配所有行:
awk '{print}' test.txt
如下这样不指定pattern也不指定action是不行的:
awk '{}' test.txt
awk '{print NR, $0}' test.txt
awk '/C/' test.txt
awk '/6/' test.txt
awk 'NR > 1' test.txt
awk 'NR > 1 {print}' test.txt
awk 'NR >= 2 && NR <= 4' test.txt
NF是字段的数量,对于空行,NF是0,对于只有空格、制表符等情况的行这个NF也是0,在如下情况下不会被输出:
awk 'NF' test.txt
当指定了分隔符为制表符(\t)时,就只有空行被去掉了:
awk -v FS="\t" '{print NF}' test.txt
awk -v FS=" " '{print NF}' test.txt
空行是除了换行符以外没有别的字符的行,如果字段分隔符被设置为“”,那么每个字符单独作为一个字段,可以看看每行的字段数:
awk -v FS="" 'NF' test.txt
awk -v FS="" '{print NF}' test.txt
输出字段分隔符默认是空格:
awk '{print $1,$2,$1,$1}' test.txt
如果要输出列的分隔符为制表符,需要特别指定:
awk -v OFS="\t" '{print $1,$2,$1,$1}' test.txt
FS和OFS也可以在BEGIN里设置
awk 'BEGIN{FS="\t"; OFS=","} {print $1,$2,$1,$1}' test.txt
awk '{SUM+=$2} END{print SUM}' test.txt
比如这里根据第一列求第二列的和,就是把第一列相同的行的第二列相加:
awk '{X[$1]+=$2} END{for(i in X) print i, X[i]}' test.txt
如果按第一列原有的顺序,可以这样:
awk 'NR==FNR {X[$1]+=$2; next} {N[$1] += 1; if(N[$1] == 1) print $1, X[$1]}' test.txt test.txt
awk '{SUM+=1} END{print SUM}' test.txt
.表示任意字符,如下表示至少要含有一个字符,计算非空行的行数,有空格和制表符的不是空行:
awk '/./ {SUM+=1} END{print SUM}' test.txt
如下命令,每一行在执行的时候,放入数组,加上1,当第二次及以后出现的时候A[$0]值大于0了,就被输出了。A[$0]++是先用这个值,然后再+1。
awk 'A[$0]++' test.txt
可以对于超过一次的行输出一次:
awk 'A[$0]++ == 1' test.txt
++A[$0]是先+1,然后再用这个值:
awk '++A[$0] == 2' test.txt
因此,去除重复的行(对于有重复的行只保留一次):
awk '++A[$0] == 1' test.txt
如果对于存在重复的行都不输入,那么就要等都运行完了之后再决定怎么输出:
awk 'NR == FNR {++A[$0]; next} A[$0] == 1 {print}' test.txt test.txt
awk [options] 'pattern {action(s)}' input-file(s)
最后的input-file(s)是输入文件,可以是多个文件(空格隔开)。命令部分用单引号 ' ' 引起来。
默认输出来stdout,可以用输出操作符>或»写到或者附加到一个文件:
awk [options] 'pattern {action(s)}' input-file(s) > output-file
如果不给定输入文件,默认为标准输入, 通过管道传入,比如:
cat input-file(s) | awk [options] 'pattern {action(s)}' > output-file
试试如下执行之后管道输入1,得到结果
echo -e "1\n2\n3" | awk '{print $1*$1}'
另外,可以键盘输入,输入如下命令回车之后等待键盘输入,输入一个数字之后回车就看到数字的平方根。需要有用Ctrl+C退出程序
awk '{print sqrt($1)}'
可以把AWK命令写到一个文件,通过-f选项调用。
awk -f script-file input-file(s)
echo "/W/ {print} /o/ {print}" > test-script.awk # 写一个测试脚本文件 echo -e "Wang\nHong\nWhong" > test-input.txt # 一个测试输入文件 awk -f test-script.awk test-input.txt
Gawk以如下顺序执行程序:
-v设置的参数,给变量赋值
[options]里边主要用到的有-v、-f:
-v在程序运行前设置变量,可以设置很多个,每个变量一个-v
-f后边接预先编好的AWK代码,同样可以有多个
不设置-v时,一些内置的变量采用默认值,比如,记录分隔符RS默认是换行符\n,字段分隔符FS默认是空格和/或制表符,对应的,有输出时的记录分隔符ORS和字段分隔符OFS。
ls -l /bin/ | awk -v OFS="," -v ORS="\t" '/awk/ {print $1,$2,$3}'
pattern可以是正则表达式、条件表达式等等的。而当有action而无pattern的时候,默认的pattern就是匹配所有的
ls -l /bin/ | awk '{print}'
ls -l /bin/ | awk '$1 ~ /l/'
ls -l /bin/ | awk '$2==2'
ls -l /bin/ | awk '$5 > 1000000'
BEGIN和END是两个特殊的pattern。如果有BEGIN{action(s)},则最开始执行的,然后才是再对每个记录执行其余的pattern-actions
ls -l /bin/ | awk 'BEGIN{print "All awk found:"} /awk/ {print}'
如果有END{action(s)},则对每个记录执行其余的pattern-actions后再执行END{action(s)}
ls -l /bin/ | awk '/awk/ {print; i+=1}END{print "Total AWK Count: "i}'
当有pattern而没有action的时候,默认action就是print,当不指定print的对象的时候,就是把所有的print出来
ls -l /bin/ | awk '/awk/'
可以有多个pattern-actions,则依次写。AWK会对每个Record都依次进行pattern的判断以及(当pattern满足时)之后后边的action。同一个action有多个命令时用花括号括起来,命令之间以半角分号(;)分隔。 如果执行完一个action之后对当前Record不再执行后边的,则用next转到下一条Record。
ls -l /bin/ | awk '/awk/ {print}' ls -l /bin/ | awk '/awk/ {print} /gwak/ {print}' # 同时满足这两个条件的被print了两次 ls -l /bin/ | awk '/awk/ {print; next} /gawk/ {print}' # 匹配awk之后转入下一条,尽管同时匹配gawk,并没有被再次print ls -l /bin/ | awk '/awk/ {print; next} /gawk/ {print} /bash/ {print}'
AWK将文本分为多个记录(Record),逐个记录进行处理。默认情况下,记录之间以换行符(\n)分隔,因此默认是逐行处理。可以自定义记录的分隔符(RS),就不局限于逐行处理了,可以把多行当作一个Record。
每个记录又分为多个字段(field),默认情况下,字段之间的分隔符(FS)为空白字符,包括空格、制表符(\t)、换行符(当RS不是换行符时,字段中的换行符也会被当做是FS)。如果自定义FS为换行符,那么每行当作一个field。 $0表示整个Record,$1表示当前Record的第一个field, $n表示第n个field. NR是当前记录的序号。 NF表示当前记录的字段数,因此$NF则表示当前记录的最后一个字段。 字段分隔符可以用-F选项设置。
输出时的字段分隔符(OFS)默认是空格,输出时的记录分隔符(ORS)默认是换行符(“\n”)。
echo -e "A:a\t1\nB:b\t2\nC:c\t3" echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk '{print $1}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk '{print $1,$2}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -v OFS="," '{print $1,$2}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -v OFS="\n" '{print $1,$2}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -F : '{print $1}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -F ":" '{print $2}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -v FS=":" '{print $1}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -v RS="\t" '{print $1}' echo -e "A:a\t1\nB:b\t2\nC:c\t3" | awk -v RS="\t" -v ORS="New_ORS" '{print $1}'
| Name(名称) | Default value(默认值) | Description(描述) |
|---|---|---|
| FS | 空格、制表符、换行符 | 输入文件字段的分隔符 |
| OFS | 空格 | 输出文件字段的分隔符 |
| RS | 换行符 | 输入文件记录的分隔符 |
| ORS | 换行符 | 输出文件记录的分隔符 |
| NR | 当前输入记录的序号,从第一个文件第一条记录算起 | |
| FNR | 当前输入记录在当前文件中的序号,从当前文件第一条记录算起 | |
| NF | 当前输入记录的总的字段的数量 | |
| BINMODE | ||
| CONVFMT | ||
| FIELDWIDTHS | ||
| FPAT | ||
| IGNORECASE | ||
| LINT | ||
| OFMT | ||
| PREC | ||
| ROUNDMODE | ||
| SUBSEP | ||
| TEXTDOMAIN | ||
| ARGV | 命令行参数 | |
| ARGC | 命令行参数个数awk 'BEGIN{for(i=0; i < ARGV; i++) print ARGV[i]}' 参数1 参数2 |
|
| ARGIND | ||
| ENVIRON | 相关的环境变量。awk 'BEGIN{print ENVIRON["HOME"]}' |
|
| ERRNO | ||
| FILENAME | ||
| FUNCTAB | ||
| PROCINFO | ||
| RLENGTH | ||
| RSTART | ||
| RT | ||
| SYMTAB |
| 分类 | 符号 | 说明 |
|---|---|---|
| 数学运算符 | + 、 - 、 * 、 / 、 % 、 ** 、 ^ | 加、减、乘、除以、取模、指数、指数 |
| 关系运算符 | == 、 != 、 < 、 ⇐ 、 > 、 >= | 等于、不等于、小于、小于等于、大于、大于等于 awk 'BEGIN{print 2 == 2 }' awk 'BEGIN{print 2 > 3 }' |
| 赋值运算符 | =、+=、-=、*=、/=、%=、**= | 赋值、加后赋值、减后赋值、乘以后赋值、除以后赋值、取模后赋值、指数后赋值awk 'BEGIN { a = 5; a = 2; print "a = "a }' awk 'BEGIN { a = 5; a += 2; print "a = "a }' awk 'BEGIN { a = 5; a -= 2; print "a = "a }' awk 'BEGIN { a = 5; a *= 2; print "a = "a }' awk 'BEGIN { a = 5; a /= 2; print "a = "a }' awk 'BEGIN { a = 5; a %= 2; print "a = "a }' awk 'BEGIN { a = 5; a ^= 2; print "a = "a }' |
++ 、 -- | +1、-1,放在变量前先加或减再使用值,放在变量后则先用变量的值再加或减awk 'BEGIN{a = 6; b = ++ a; print "a="a"\nb="b"\n" }' awk 'BEGIN{a = 6; b = a ++; print "a="a"\nb="b"\n" }' awk 'BEGIN{a = 6; b = -- a; print "a="a"\nb="b"\n" }' awk 'BEGIN{a = 6; b = a --; print "a="a"\nb="b"\n" }' |
|
| 逻辑运算 | &&、||、! | 且、或、非 |
| 三元运算符 | 条件表达式 ? 陈述1 : 陈述2 | 条件表达式结果为true时执行陈述1,不然执行陈述2 |
| 一元算符 | + 、- | 乘以 +1 、乘以 -1 |
| 数组元素运算符 | in | 存在于 awk 'BEGIN {arr["A"] = 1; arr["X"]; print "A" in arr}' |
| 正则表达式运算符 | ~、!~ | 匹配、不匹配 |
这与其它编程语言的类似,这里以例子的形式介绍。
awk 'BEGIN{for(i=1; i < 5; ++i) print i }'
awk 'BEGIN{for(i in ENVIRON) print i " is " ENVIRON[i] "\n" }'
awk 'BEGIN {i = 1; while (i <= 4) { print i; ++i } }'
awk 'BEGIN {i = 1; do { print i; ++i } while (i <= 4) }'
能够进行等值比较或者正则匹配。 各个分支需要用break来终止。
awk '{ switch($1){ case 1: print "星期一" break case 2: print "星期二" break case 3: print "星期三" break case 4: print "星期四" break case 5: print "星期五" break case 6: print "星期六" break case 7: print "星期日" break default: print "请输入数字1~7!" break } }'
这里没有给定输入文件,接受键盘输入,运行时输入数字,就打印出星期。试试看。最后用Ctrl+c来终止程序的运行。
awk 'BEGIN { X = 0 for (i = 0; i < 10; ++i) { X += i*5 print "i=", i, ", X =", X if (X > 50) break } }'
当满足条件的时候break,不再继续执行。
看看下边continue不同的位置的效果:
awk 'BEGIN { for (i = 1; i <= 50; ++i) { if (i % 5 == 0) print i else continue print "OK, i=", i } }'
这时候,当i不能被5整除的时候,就跳到下一循环了,因此,只有当i能被5整除的时候会执行后边的print “OK, i=”, i。
awk 'BEGIN { for (i = 1; i <= 50; ++i) { if (i % 5 == 0) print i continue print "OK, i=", i } }'
这时候当I能够被5整除的时候print i,然后不管i怎么样都会continue到下一个循环,后边的print “OK, i=”, i始终不会被执行。
awk 'BEGIN { for (i = 1; i <= 50; ++i) { if (i % 5 == 0) { print i continue } print "OK, i=", i } }'
这个时候,只有当i能够被5整除的时候,才会continue到下一个循环,这样当i不能被5整除的时候,会执行后边的print “OK, i=”, i。
next是转到下一条记录。 continue和break是用于for、while、do…while、switch等循环。
ls -l /bin/ | awk '/awk/ {print} /gawk/ {print}' ls -l /bin/ | awk '/awk/ {print; next} /gawk/ {print}'
nextfile 立即停止当前语句的后续操作,并直接转到下一个文件,并进入循环顶部。
直接退出AWK程序。
ls -l /bin/ | awk '/awk/ {print; exit} /gawk/ {print}'
awk 'BEGIN { for (i = 1; i <= 50; ++i) { if (i % 5 == 0) { print i exit } print "OK, i=", i } }'
exit后边加上数字,指定退出状态码
echo $? awk 'BEGIN{exit 10}' echo $?
if 单独使用
awk '{if($1==1) print "星期一"}'
键盘输入1能够得到输出,其它情况下没有输出。
if…else, 加上else之后else后边必须要有action,不然会报错。
awk '{ if($1==1) print "星期一" else print "输入数字1,打印星期一" }'
键盘输入1能够得到输出,其它情况下输出“输入数字1,打印星期一”。
能够多个if…else if…else……
awk '{ if($1 == 1){ print "星期一" } else if($1 == 2){ print "星期二" } else if($1 == 3){ print "星期三" } else if($1 == 4){ print "星期四" } else if($1 == 5){ print "星期五" } else if($1 == 6){ print "星期六" } else if($1 == 7){ print "星期七" } else { print "请输入数字1~7!" } }'
printf用于格式化的打印,语法为:
printf 格式, 项目1, 项目2, ……
其中格式限定符以%开始,以一个格式控制字母结尾,格式限定符按顺序与后边的项目相对应。%s、%f、%d、%%等等比较常用的要记住其用法。
| 格式限定符 | 说明 |
|---|---|
| %s | 字符串。 |
| %f | 十进制浮点数字。%n.mf, 小数点后保留m位,整个数字占n位,长度不够则前边加空格;%0n.mf,位数不够前边用0补齐。 awk 'BEGIN{printf "%10.6f", 9.99}' awk 'BEGIN{printf "%010.6f", 99.99}' |
| %d、%i | 十进制整数部分. awk 'BEGIN{printf "%d", 9.99}' awk 'BEGIN{printf "%03d", 9.99}' |
| %% | 一个百分号: ‘%’. |
| %a、%A | |
| %c | Print a number as a character; awk 'BEGIN{printf "%c", 88}' |
| %e、%E | Print a number in scientific (exponential) notation. awk 'BEGIN{printf "%4.3e", 999.99}' awk 'BEGIN{printf "%5.4E", 9999.99}' |
| %F | |
| %g、%G | Print a number in either scientific notation or in floating-point notation, whichever uses fewer characters. |
| %o | Print an unsigned octal integer. |
| %u | |
| %x、%X |
printf 并不会像print那样自动加上换行符(记录分隔符)。
sprintf和printf的格式语法一致,不同的是printf是将得到字符串打印出来,而sprintf得到的值可以赋值给一个变量。
awk 'BEGIN{A=sprintf("%010.6f", 99.99); print(A)}'
需要注意的是,这里sprintf后边的括号是不可以省略的。
awk里print的内容可以通过管道传给sh执行。如下程序执行后,输入shell的命令,得到命令的的结果:
awk '{print $0 | "sh"} END {close("sh")}'
如下程序执行后,输入一个文件名,将这个文件重名问全部字母都大写的文件:
awk '{printf "mv %s %s\n", $0, toupper($0) | "sh"} END {close("sh")}'
getline 有多个变体,这些getline不同的使用方式效应不同,也就是改变的变量不同。归纳如下表1):
| Variant | Effect | awk / gawk |
|---|---|---|
getline | Sets $0, NF, FNR, NR, and RT | awk |
getline var | Sets var, FNR, NR, and RT | awk |
getline < file | Sets $0, NF, and RT | awk |
getline var < file | Sets var and RT | awk |
command | getline | Sets $0, NF, and RT | awk |
command | getline var | Sets var and RT | awk |
command |& getline | Sets $0, NF, and RT | gawk |
command |& getline var | Sets var and RT | gawk |
getline返回值可以用于逻辑运算:正确读到数据,则返回值为1;读取数据遇到EOF,则返回值0;读取遇到错误则为负数,-1表示文件无法打开,-2表示IO操作需要重试;遇到错误时用变量ERRNO描述错误。
这时候getline读取下一个记录并将它拆分成字段,这个getline命令设置了NF、NR、FNR、RT以及$0的值。
echo -e "A\t10\nB\t20\nC\t30\nD\t40" | awk '{print $0; getline}' echo -e "A\t10\nB\t20\nC\t30\nD\t40" | awk '{print $0; if((getline) > 0) print "NR="NR, $0}'
可以看到getline设置了NR之后,再次print $0就到下下一个记录了。
getline到变量X:
echo -e "A\t10\nB\t20\nC\t30\nD\t40" | awk '{print $0; getline X; print X}' echo -e "A\t10\nB\t20\nC\t30" | awk '{print $0; getline X; print X}' echo -e "A\t10\nB\t20\nC\t30" | awk '{print $0; if((getline X) > 0) print X}'
可以看到,在最后一个记录的时候,getline不再能够改变X的值。当把getline放到if后边作为条件的时候,依然会执行getline。
先建两个测试文件:
echo -e "A\t10\nB\t20\nC\t30\nD\t40" > test_g.txt echo -e "a\t01\nb\t02\nc\t03\nd\t04" > test_i.txt
可以看到,每次运行都在上次运行的下一条记录:
awk '{ getline < "test_g.txt" print $0, "NR="NR, "FNR="FNR, "NF="NF }' test_i.txt
可以看到,getline到一个变量的时候,不改变$0
awk '{ getline X < "test_g.txt" print X, "NR="NR, "FNR="FNR, "NF="NF print $0, "NR="NR, "FNR="FNR, "NF="NF }' test_i.txt
awk '{ getline X < "test_g.txt" print X, "NR="NR, "FNR="FNR, "NF="NF print $0, "NR="NR, "FNR="FNR, "NF="NF }' test_i.txt test_i.txt
结合printf可以将两个文件逐行并列地放到一行:
awk '{ printf "%s\t", $0 getline < "test_g.txt" print $0 }' test_i.txt
在getline之前,$0是test_i.txt文件里的记录,printf打印出来后边加个制表符\t,getline之后,$0改变了,print的时候自动加上了换行符。
可以在BEGIN里连续多次getline完成对一个文件的处理:
awk 'BEGIN{ while(1){ if((getline < "test_g.txt") > 0){ print $0, "NR="NR, "FNR="FNR, "NF="NF } else { break } } }'
可以将一个文件的信息存到一个数组里,然后再使用:
awk 'BEGIN{ while(1){ if((getline < "test_g.txt") > 0){ A[$1]=$2 } else { break } } } { print $0, A[toupper($1)] }' test_i.txt
系统命令需要用引号引起来,然后是需要close这个命令。
awk 'BEGIN{ "date" | getline close("date") print $0 }'
int(n)函数获取数字n的整数部分
awk 'BEGIN{print int(3.5678)}' awk 'BEGIN{print int(-3.5678)}'
log(n) 求数字n的自然对数
awk 'BEGIN{print log(100)}'
sqrt(n) 求数字n的正的平方根
awk 'BEGIN{print sqrt(100)}'
sin(n) 求n的郑玄,n为弧度数。同样有cos(n)求余弦。
awk 'BEGIN{print sin(3.14159265)}' #
rand() 产生0~1之间的随机数字。srand(n)初始化随机数字产生的seed,使用了srand而不设置具体数值, 每次运行得到的结果不同:可以看到每次运行得到的数字是一样的。不加上rand()则相当于设置了srand(1):
awk 'BEGIN{print rand()}' awk 'BEGIN{print rand()}' awk 'BEGIN{srand(1) print rand()}' awk 'BEGIN{srand(1) print rand()}' awk 'BEGIN{srand(2) print rand()}' #如下可以产生10个随机数字: awk 'BEGIN{for(i=1; i<= 10; i++) print rand();}' awk 'BEGIN{for(i=1; i<= 10; i++) print rand();}' # 使用了srand而不给参数,使用当前时间,每次运行结果都不一样 awk 'BEGIN{srand( ); print rand()}' awk 'BEGIN{srand( ); print rand()}' # 设置好srand,以便产生的随机数是可重复的: awk 'BEGIN{srand(1); for(i=1; i<= 10; i++) print rand();}' awk 'BEGIN{srand(1); for(i=1; i<= 10; i++) print rand();}' awk 'BEGIN{srand(5); for(i=1; i<= 10; i++) print rand();}' awk 'BEGIN{srand(5); for(i=1; i<= 10; i++) print rand();}'
match(s,r)测试字符串s是否包含匹配正则表达式r的字符串,返回第一次匹配的开始位置,如果不存在则返回值为0。
awk 'BEGIN{print match("WangHong", "Ho")}'
index(str1, str2) 在str1中str2第一次出现的位置,第一个字符开始的位置,如果不存在,则为0。index中str2不能是正则表达式,这是与match函数不同的。
awk 'BEGIN{print index("WangHong", "Ho")}'
length(str) 计算字符串str的长度
awk 'BEGIN{print length("WangHong")}'
substr(s, p, n) 从p位开始对字符串s取n个字符串,如果n未指明,则取出后边所有的字符
awk 'BEGIN{print substr("WangHong", 5, 10)}'
tolower(str)将字符串str中的字母转为小写,与之对应的,toupper(str)为转为大写
awk 'BEGIN{print tolower("WangHong")}'
split(string, array, fieldsep) 将字符串string以fieldsep为分隔符拆分为字段,存为数组array,片段依次存为array[1]、array[2]、……
awk 'BEGIN{split("WangHong", X, "n"); for(i in X) print X[i]}'
sub(r, t, s)将字符串s中第一次出现的匹配正则表达式r的部分替换为t,如果不指定s,则对$0进行替换。
awk 'BEGIN{XM="WangHong"; sub("ng", "XX", XM); print XM}'
gsub(r, t, s)将字符串s中所有匹配正则表达式r的部分都替换为t,ruguo不指定s,则对$0进行替换。
awk 'BEGIN{XM="WangHong"; gsub("ng", "XX", XM); print XM}'
sprintf(format, expression1, …)返回格式化的字符串,与printf不同的是sprintf并不打印出来。
awk 'BEGIN{Pi = sprintf("pi ≈ %.2f", 22/7); print Pi}'
比如现在有一个fasta文件,另有一个文件记录我们要提取的序列的ID,这时候使用AWK就会非常简单,示例文件test.fa与ID.txt
>ID01 some description 01 GCGCGCGGTCACGTTTTCCACTATGTGACAGCGGAGGGCGACGCGGCGGCAGCGGCGCTACTGGGACTAG >ID02 some description 02 CGGCTCCGGGCGGCTGCGGCGCAGGCCGAGCGCACCAAGTGACGGGCCGAGCAAGGGACAGACGCGCGGG TTGACGCGGCGAAGCGCTTATTCCAGAGCCCGACATGAATGGATACGTGGACTTCTCCCCAAGTCCCACC AGTCCCACCAAGGAGCCAGGGGCACCTCAGCCCACCCAGGCTGTGCTCCAGGAAGACGTGGACATGAGCA GTGGCTCCAGCGGAAACGAGAACTGCTCCACGGGACGGGACTCTCAGGGCAGTGACTGCGACGACAATGG >ID03 some description 03 GAAGGAGCTGCGGATGCTCGTGGAATCTTCCAACACTCACCCCAGCCCTGATGATGCCTTCAGACTCATG ATGACAGAGGCAGAGCACAACCCCTCCACGAGCGGCTGCAGTAGTGAGCAGTCTGCCAAAGCTGACGCAC >ID04 some description 04 ACAAAGAACTGATAAGGACCCTGAAGGAGCTGAAGGTCCACCTCCCTGCAGACAAGAAGGCCAAGGGGAA >ID05 some description 05 GGCCAGCACGCTGGCAACCTTGAAGTATGCCCTGCGGAGCGTGAAGCAGGTGAAGGCTAATGAGGAGTAC >ID06 some description 06 TACCAGCTGCTAATGTCCAGTGAGAGCCAGCCCTGCAGTGTGGATGTGCCTTCCTACAGCATGGAGCAGG TTGAGGGCATTACCTCCGAGTATATCGTGAAGAACGCGGATATGTTTGCTGTGGCTGTGTCCCTGGTTTC >ID07 some description 07 TGGGAAGATCCTGTACATCTCTAACCAAGTGGCCTCCATCTTTCACTGTAAGAAGGACGCCTTCAGTGAT >ID08 some description 08 GCCAAGTTTGTGGAGTTCCTGGCTCCTCATGACGTCAGTGTGTTCCACAGCTACACCACCCCTTACAAGC >ID09 some description 09 TTCCGCCCTGGAGTGTGTGCAGCGGCTTAGATTCTTTCACTCAGGAGTGCATGGAGGAGAAATCTTTTTT
ID02 ID04 ID09
自己创建上边两个文件用于测试。 获取ID.txt里的ID的序列的代码如下,试试看是不是得到了自己想要的结果:
awk -v RS=">" 'NR > 1 && NR == FNR {IDs[$1];next} $1 in IDs {printf ">"$0}' \ <(awk '{print ">"$0}' ID.txt) test.fa
这里以>作为记录的分隔符,这样每条序列就是一个记录,默认是以空格分隔字段,ID正好是第一个字段。而输出时默认的记录分隔是换行符,因此也要指明ORS。
因为ID.txt里的ID之间的分隔是换行符,为了能够使用前边设置的-v RS=“>”,这里给它加上, 也就是<(awk '{print “>“$0}' ID.txt)所干的活。
这里有两个输入文件,NR以所有文件作为一个整体进行连续计数,而FNR对每个文件从1开始,所以对于第一个文件,FNR是等于NR的,从第二个文件开始,FNR就和NR不相等了。
所以,这里运行过程就是对第一个文件,把每个记录的第一个字段作为数组(阵列)IDs的索引(Index)而记录下来,然后next到下一条记录,等到了第二个文件的时候,FNR就不等于NR了,于是就执行后边的,判断第一个字段是否能够在字典中找到,能找到就把整个记录print出来。
这里使用printf,因为print默认在每个记录后边加上一个RS,而fasta文件的>是加在前边的。
比如还是用上边例子中的test.fa文件:
awk -v RS=">" -v FS="\n" -v OFS="\t" ' BEGIN{print "transcript_id\ttranscript_length\tgc_count\tgc_content"} NR > 1 { Tid = $1; $1 = ""; gsub("\t", "", $0); nnN = length($0); gsub("G|C", "", $0); gcN = nnN - length($0); gcR = sprintf("%.4f", gcN/nnN); print Tid,nnN,gcN,gcR}' test.fa \ > test.transcript_info.tsv
其逻辑是,以>来分隔各条序列,那么第一条记录(Record)是空的,序列的信息从第二条记录开始,字段(Field)则以换行符\n分隔,第一个字段($1)是序列的ID,后边的是序列。记录下之后把$1变成空之后,$0就只包含序列和字段分隔符了(这时候的分隔符是输出的分隔符OFS\t);然后去掉\t之后就是整条序列了,可以算得整条序列的长度;然后将G和C去掉,然后其长度就是没有GC的长度,减一下就可以得到GC的数量;最后可以算得GC含量。
得到的结果如下:
transcript_id transcript_length gc_count gc_content ID01 some description 01 70 48 0.6857 ID02 some description 02 280 184 0.6571 ID03 some description 03 140 81 0.5786 ID04 some description 04 70 37 0.5286 ID05 some description 05 70 41 0.5857 ID06 some description 06 140 75 0.5357 ID07 some description 07 70 33 0.4714 ID08 some description 08 70 38 0.5429 ID09 some description 09 70 34 0.4857
如果transcript_id里边含有制表符,那么可以把制表符替换成别的,不然将会影响列的区分。当然也可以使用别的符号来分隔表格。
当然也可以不改变$0来完成任务,但是运行起来似乎会慢一些:
awk -v RS=">" -v FS="\n" -v OFS="\t" ' NR > 1 { Seq = "" for(i=2; i<=NF; i++) Seq = Seq$i; nnN = length(Seq); gsub("G|C", "", Seq); gcN = nnN - length(Seq); gcR = sprintf("%.4f", gcN/nnN); print $1,nnN,gcN,gcR}' test.fa \ > test.transcript_info.tsv
比如,我有一个81415条序列的fasta文件(比较叫ORFs.fa),我想要拆分开用于功能注释,那么可以这样子拆分成每个含1000条序列的文件,文件名以:
awk -v RS=">" -v ORS="" 'NR != 1 { FileName=sprintf("ORFs_%02d.fa", int((NR-1)/1000)); print ">"$0 > FileName }' ORFs.fa
本网页已被访问次数(Times visited):12369; 最后编辑时间(Last modified): 2024/11/12 16:21