awk 截取部分字符串_[Linux][工具] Linux文本批量处理工具系列(3)awk

a6b377bd42470a578d3f9e7be7fd6ec7.png

awk 是这样一个工具:它可以执行更加复杂的文本处理,将庞大的文本分割成行,逐行读入,同时可以对每行的字符进行切片,并对切片后的独立字符串进行制定的运算、排序或编辑工作。

配合其它的文本处理工具,比如之前讲过的 grep 和 sed,awk 可以对它们输出的结果进行编辑并输出为指定的格式。三者的配合可以帮我们处理很多特定格式的数据,尤其是 bed,gtf 这种每列之间由空格或制表符分割的数据格式,更可以让它们大显身手。之后我会有一篇笔记专门讲三者同时应用的一些例子。

我们在 Linux 上使用的 awk,一般是 awk 的 GNU 版本 gawk。

awk 的工作流程

首先,awk 的语句块被划分为三部分:BEGIN,BODY 和 END,三者有相同的语法为:

BEGIN/{pattern}/END {awk command} #{pattern} 为 BODY 语法

BEGIN 和 END 均为可选部分,一个简单的 awk 命令可以只由 BODY 部分组成,三者的流程关系可由下图所示:

403ea07dd3a58adfb90c53c2d4b4b133.png

awk 的工作流程可分为三步:

  1. 通过 BEGIN 部分对将要读入的文件或标准输入进行预处理,读取一行并输入到 BODY 部分
  2. BODY 语块对输入的这一行进行处理,依次执行 awk command,执行过后输出该行并读取下一行
  3. 在所有的行都被处理后,处理好的文本被输入到 END 语块,执行 END 部分的命令,并输出

所以说,我们常听人说的 awk 按行处理数据是针对 BODY 语块说的,BEGIN 和 END 语块在整个 awk 命令中都只执行一次。

基本用法

刚才说过,BEGIN 和 END 语块是可选的,所以最简单的 awk 命令可以被写为:

 awk [options] 'pattern{action}' file

如果你写好了脚本,就变为(profile 为你写好的脚本):

gawk [options] -f progfile file

如果说awk 中按“行”处理是很重要的一个概念,那么分隔符决定的“列”就是 awk 里实现文本分割的另一个重要概念。你可以把它想象成一个表格文件,各个列之间都有相同的分隔符分隔,比如 csv 用的逗号,bed 文件用的 tab (制表符)

-F :指定分隔符

如果不进行指定分隔符,awk 将自动以空格或者 tab 作为文档的分隔符进行分割

用大鼠的 rn6 GTF 文件举个例子,它的前五行为版本信息

$ head -5 Rattus_norvegicus.Rnor_6.0.96.gtf
#!genome-build Rnor_6.0
#!genome-version Rnor_6.0
#!genome-date 2014-07
#!genome-build-accession NCBI:GCA_000001895.4
#!genebuild-last-updated 2017-01

如果我们不设定指定的分隔符,直接用 awk 打印前五行的第一列,为($1 表示这一行被分隔符分割的第一个语段,我写第一列是为了方便理解):

$ head -5 Rattus_norvegicus.Rnor_6.0.96.gtf |awk  '{print $1}'
#!genome-build
#!genome-version
#!genome-date
#!genome-build-accession
#!genebuild-last-updated

如果我们设定分隔符为"-",那么输出的结果为:

$ head -5 Rattus_norvegicus.Rnor_6.0.96.gtf |awk -F - '{print $1}'
#!genome
#!genome
#!genome
#!genome
#!genebuild

正则表达式

awk 可以使用正则表达式,对指定的区域进行提取,比如我们刚刚用到的 $1,$ 可以帮助我们分割指定的列,这些列在 awk 中被称为“域”,其中 $0 代表的是一整行。通过 -F 参数,我们可以指定域之间的分割符号。

awk 所用的大部分元字符和 sed 是共享的,除了对各个区域的分割外 awk 还可以实现和 grep 类似的匹配功能。

使用 . (对就是这个点)我们可以用它指代任意的单字符:

举个例子:

我们试着用 awk 找一下123,258,462,127,562 和 298 中以2开头以8结尾的

$ echo -e "123n258n462n127n562n298" | awk '/2.8/'
258
298

我们可以用中括号来代替一定范围内的数字,或者多个可能的字符,就和其他的正则表达式一样:

$ echo -e "123n258n462n127n562n298" | awk '/[0-9][0-9]2/'
462
562

通过 ^ 和 $ 可以指定我们搜索字符的位置:

# 以2 开头的数字
$ echo -e "123n258n462n127n562n298" | awk '/^2/'
258
298
# 以7 结尾的数字
$ echo -e "123n258n462n127n562n298" | awk '/7$/'
127

其他的正则表达式符合也可以被用在 awk 命令里面,现在的新问题是,用这样的方法我们只能按行匹配。

也就是说由于我们直接用 awk 去匹配字符串,导致只有在和我们输入的要求和整个行的内容完全一致的情况下,才能够匹配到我们想要的结果。如果我们 echo 的分隔符不是换行符而是制表符的话,这样的命令是无法在这一行中分辨我们想找的字符串的:

$ echo -e "123t258t462t127t562t298" | awk '/2.8/'
123     258     462     127     562     298

除了刚刚我说到的和其他正则表达式相同的字符外,awk(gawk) 还有一些专用的元字符:

Y 匹配一个单词开头或者末尾的空字符串。
B 匹配单词内的空字符串。
< 匹配一个单词的开头的空字符串,锚定开始。
> 匹配一个单词的末尾的空字符串,锚定末尾。
w 匹配一个字母数字组成的单词。
W 匹配一个非字母数字组成的单词。
‘ 匹配字符串开头的一个空字符串。
' 匹配字符串末尾的一个空字符串。

想一篇笔记全讲完 awk 几乎是痴人说梦,如果有兴趣完整的学习 awk 的朋友可以自己看这个:

http://www.gnu.org/software/gawk/manual/gawk.html#Options

我下面参考的几篇 blog 也非常值得看。

我的笔记主要是给生信分析的朋友看的...当然能够熟练运用 awk 肯定是好处多多,我这篇算是抛砖引玉了。下一篇这个系列的笔记我会写一些我常用的 awk + sed + grep 的命令,完成一些数据格式的转换和一些好玩或实用的操作

参考:

AWK正则表达式 - Awk教程™

https://blog.csdn.net/j2eewaibao/article/details/83174604

linux文本处理三剑客之awk

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页