设为首页 加入收藏

TOP

Linux:管道命令与文本处理三剑客(grep、sed、awk)(四)
2023-07-23 13:34:05 】 浏览:81
Tags:Linux 管道命 文本处 grep sed awk
:35 - 13:17 (00:42) wtmp begins Tue Apr 10 08:54:45 2018

若我想取出账号与登陆者的IP,且账号与IP之间以[Tab]隔开,则会变成这样:

root@orion-orion:~ last -n 3 | awk '{print $1 "\t" $3}'                                                        
root    10.249.45.37
root    10.249.45.37
root    10.249.45.37

注意,awk的所有后续操作都是以单引号括住的,而awk的格式内容如果想要以print打印时,记得将非变量的文字部分使用双引号括起来,因为单引号已经是awk命令的固定用法了。此外,因为这里无论哪一行我们都要处理,因此就不需要有条件类型的限制。

另外,由上面的例子我们看到,在awk的括号内,每一行的每个字段都有变量名称($1$2等)。在上面的例子中,root位于第1栏,故其变量名称为$1;而10.249.45.37是第3栏,故它是$3,后面以此类推。还有个变量比较特殊,那就是$0,它表示一整行数据。由此可知,刚刚上面5行当中,整个awk的处理流程就是:

  1. 一次性读入第1行整行的数据并存入$0,然后将其拆分为多个字段并写入$1$2$3等变量当中。
  2. 根据条件类型的限制,判断是否需要进行后面的操作(在上面这个例子中没有条件类型)。
  3. 完成所有操作与条件类型。
  4. 若还有后续行的数据,则重复上面1~3的步骤,直到所有的数据都读完为止。
    经过这样的步骤,我们看到了awk以行为一次处理的单位,而以字段为最小的处理单位。好了,那么如何快速地获得我们的数据有几行几列呢?这就需要awk的内置变量的帮忙。
变量名称 代表意义
NF 每一行(也即$0)所拥有的字段总数
NR 目前awk所处理的是第几行数据
FS 目前的分割字符,默认是空格键

我们继续以上面last -n 3的例子来做说明,如果我想要:

  • 列出每一行的账号(也就是$1);
  • 列出目前处理的行数(就是awk内的NR变量);
  • 并且说明该行有多少字段(也就是awk内的NF字段);

则可以这样:

root@orion-orion:~ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}'
root     lines: 1        columns: 10
root     lines: 2        columns: 10
root     lines: 3        columns: 10
         lines: 4        columns: 0
wtmp     lines: 5        columns: 7

注意,在awk内的NRNF等变量要用大写,且不需要有美元符号$

接下来我们来看一看所谓的“条件类型”。

awk 的逻辑运算字符
既然要用到“条件”的类别,那么自然就需要一些逻辑运算,如下所示:

运算单元 代表意义
> 大于
< 小于
>= 大于或等于
<= 小于或等于
== 等于
!= 不等于

注意,逻辑运算即所谓的大于、小于等于等判断式上面,习惯上用==而不是=来表示,=符号在awk操作这里留给了变量赋值用。

我们来看下面一个例子。比如在/etc/passwd中是以冒号:来作为字段的分隔,该文件中第一字段为账号,第三字段为UID。如下所示:

root@orion-orion:~ cat /etc/passwd | less                                                                      
root:x:0:0:root:/root:/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
dnsmasq:x:115:65534:dnsmasq,,,:/var/lib/misc:/bin/false

那假设我要查看第三栏小于10的数据,并且仅列出账号与第三列,那么可以这样做:

root@orion-orion:~ cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
root:x:0:0:root:/root:/bin/zsh   
daemon   1
bin      2
...

诶,不过怎么第一行没有正确地显示出来?这是因为我们在读入第一行的时候,那些变量$1$2等等默认还是以空格键做为分割,所以虽然我们定义了FS=":",但却仅能在第二行后才开始生效。那怎么办呢?我们可以预先设置awk的变量,利用BEGIN这个关键词,这样做:

root@orion-orion:~ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}'
root     0
daemon   1
bin      2
...

接下来我们来看如何用awk来完成计算功能。

假设我们有一个薪资数据表文件为pay.txt,内容如下:

root@orion-orion:~ cat pay.txt
Name    1st     2nd     3th
VBird   23000   24000   25000
DMTsai  21000   20000   23000
Bird2   43000   42000   41000

如何来计算每个人1st2nd3th的总额呢?而且我们还需要格式化输出。我们可以这样考虑:

  • 第一行只是表头,所以第一行不进行求和而仅需要对表头进行打印(也即NR==1时处理)。
  • 第二行以后进行求和(NR>=2以后处理)。
root@orion-orion:~ cat pay.txt | \
awk 'NR == 1 {printf "%10s %10s %10s %10s %10s\n", $1, $2, $3, $4, "Total"} \
NR >= 2 {total = $2 + $3 + $4; \
printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'
      Name        1st        2nd        3th      Total
     VBird      23000      24000      25000   72000.00
    DMTsai      21000      20000      23000   64000.00
     Bird2      43000      42000      41000  126000.00

上面的例子有几个重要事项应该要先说明:

  • awk的命令间隔:所有awk的操作,亦即在{}里的操作,如果有需要多个命令辅助时,可利用分号;间隔。
  • 逻辑运算中,如果是“等于”的情况,请务必使用==
  • 格式化输出时,在printf的格式设置中,务必加上\n,才能分行(这里注意可以和Python的print函数和shell的echo函数做对比,此二者自带换行);
  • 与bash shell中的变量不同,awk中的变量可以直接使用,不需要加上$符号。

另外,awk的操作内{}也是支持if( 条件 )的,比如上面的命令也可以写为:

root@orion-orion:~ cat pay.txt | \
awk '{if (NR == 1) printf "%10s %10s %10s %10s %10s\n", $1, $2, $3, $4, "Total"}
首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇CentOS7---部署Tomcat和安装Jpress 下一篇linux vi命令详解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目