• 拾遗之”三元运算”与”打印奇偶行”


    三元运算

    还记的我们在学习awk的 "if..else" 结构时,举的例子吗?我们来回顾一下。

    在centos6中,我们可以判断用户的UID是否小于500,如果用户的UID大于500,则用户为普通用户,如果用户的UID小于500,则用户为系统用户。

    所以,我们可以通过awk的 "if...else结构",判断用户的UID范围,从而判断出用户属于哪种用户类型,示例如下

    [root@server01 ~]# awk -F: '{if($3<500){usertype="系统用户"}else{usertype="普通用户"};print $1,usertype}' /etc/passwd
    root 系统用户
    bin 系统用户
    daemon 系统用户
    adm 系统用户
    lp 系统用户
    sync 系统用户
    shutdown 系统用户
    halt 系统用户
    mail 系统用户
    operator 系统用户
    games 系统用户
    ftp 系统用户
    nobody 系统用户
    systemd-network 系统用户
    dbus 系统用户
    polkitd 普通用户
    sshd 系统用户
    postfix 系统用户
    chrony 普通用户
    ntp 系统用户
    tss 系统用户

    正如上例所示,我们使用"if...else"结构,对usertype变量进行了赋值,如果用户的UID小于500,则对usertype变量赋值为"系统用户",否则则赋值usertype变量为"普通用户",最后打印出用户名所在的列与usertype变量的值。

    其实,我们可以使用三元运算,替换上例中的"if...else"结构语句,示例如下

    [root@server01 ~]# awk -F: '{usertype=$3<500?"系统用户":"普通用户"; print $1,usertype}' /etc/passwd
    root 系统用户
    bin 系统用户
    daemon 系统用户
    adm 系统用户
    lp 系统用户
    sync 系统用户
    shutdown 系统用户
    halt 系统用户
    mail 系统用户
    operator 系统用户
    games 系统用户
    ftp 系统用户
    nobody 系统用户
    systemd-network 系统用户
    dbus 系统用户
    polkitd 普通用户
    sshd 系统用户
    postfix 系统用户
    chrony 普通用户
    ntp 系统用户
    tss 系统用户

    正如上例所示,红线标注部分则使用了三元运算的语法,代替了之前"if...else"的语法,而三元运算的语法如下:

    条件 ? 结果1 : 结果2

    上述语法表示,如果条件成立,则返回结果1,如果条件不成立,则返回结果2。

    而上例中,"$3<500"就是上述语法中的"条件","系统用户"就是上述语法中"?"后面的"结果1","普通用户"就是上述语法中":"后面的"结果2"  ,同时,在上例中我们使用usertype变量接收了三元运算后的返回值,所以,当条件成立时,usertype变量被赋值为"系统用户",当条件不成立时,usertype变量被赋值为"普通用户"。

    是不是很方便?其实,三元运算还有另外一种使用方式,示例如下

    [root@server01 ~]# awk -F: '{$3<500?a++:b++} END{print a,b}' /etc/passwd
    19 2

    我们通过上述命令,统计出了,系统用户有42个,普通用户有7个,上图中红线标注的用法可以理解为三元运算的另一种语法。如下

    表达式1 ? 表达式2 : 表达式3

    上述语法表示,如果表达式1为真,则执行表达式2,如果表达式1为假,则执行表达式3

    而上例中,"$3<500"即为表达式1,"a++"即为表达式2,"b++"即为表达式3

    也就是说,当每遇到一个UID小于500的用户,我就对变量a加1,否则我就对变量b加1,从而算出了系统用户与普通用户的数量,最后再END模式中输出了变量a与变量b的值。

    是不是很容易理解?你一定已经明白了。

    打印奇偶行

    如果我们想要使用awk打印文本中的奇数行或者偶数行,则是非常简单的。

    我们先来看看怎样使用awk打印奇数行或偶数行,然后再结合示例解释原理,所以看不懂没关系,后面会有解释。

    [root@server01 ~]# cat test 
    第 1 行
    第 2 行
    第 3 行
    第 4 行
    第 5 行
    第 6 行
    第 7 行
    第 8 行
    第 9 行
    第 10 行
    第 11
    [root@server01 ~]# awk 'i=!i' test
    第 1 行
    第 3 行
    第 5 行
    第 7 行
    第 9 行
    第 11
    [root@server01 ~]# awk '!(i=!i)' test
    第 2 行
    第 4 行
    第 6 行
    第 8 行
    第 10

    正如上例所示,test12文件中有11行文本,我们可以使用非常简洁的awk命令,打印出了奇数行或者偶数行。

    但是如果我们想要彻底搞明白原理,则需要搞明白如下两个知识点(后面会有更详细的解释)

    1、在awk中,如果省略了模式对应的动作,当前行满足模式时,默认动作为打印整行,即{print $0}。

    2、在awk中,0或者空字符串表示"假",非0值或者非空字符串表示"真"

    上述两个知识点是什么意思呢?我们慢慢聊。

    在之前介绍awk模式的文章中提及过,模式可以理解为条件,如果当前行能与模式匹配,则会执行对应的动作。示例如下

    [root@server01 ~]# awk '/1/{print}' test 
    第 1 行
    第 10 行
    第 11
    [root@server01 ~]# awk '$2>10{print}' test 
    第 11

    我们发现,当使用了模式时,如果省略了对应的动作,会默认的输出整行。

    也就是说,当使用了模式时,如果省略了模式对应的动作,默认动作为"{print $0}"

    当然,"空模式"与"BEGIN/END模式"除外。

    这就是第1个知识点的含义,我想你应该明白了,那么我们来聊聊第2个知识点。

    在awk中,0或者空字符串表示"假",非0值或者非空字符串表示"真",什么意思呢?我们还是可以从模式说起,"模式"可以理解为"条件",当条件成立,则为真,当条件不成立,则为假,所以,当模式为真时,则会执行对应的动作,当模式为假时,则不会执行对应的动作。

    那么,我们能不能直接把模式替换为"真"或者"假"呢?我们来试试。

    [root@server01 ~]# cat test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '{print $0}' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '1{print $0}' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '2{print $0}' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '2' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '0{print $0}' test3
    [root@server01 ~]# awk '0' test3

    上例中,命令1使用了"空模式",也就是说,每一行都满足模式,每一行经过"空模式"匹配以后结果都是"真",所以每一行都会执行对应的动作。

    命令2中,原来"模式的位置"被替换为了数字"1",我们可以把数字"1"理解成一种模式匹配后的结果,而1是非零值,刚才说过,在awk中非零值表示真,所以,"1"表示"真",  换句话说就是模式的匹配结果为真,模式成立则会执行对应的动作,而命令2中,对应的动作为打印整行。

    命令3 与 命令2 同理,在命令3中,  数字"2"为非零值,表示真,可以理解为:模式的匹配结果为真,则会执行对应的动作,聪明如你一定想到了,数值"2"可以换做任何非0值或者非空字符串。

    命令4中,数字"2"为非零值,表示模式为真,而之前说过,当使用模式时,可以省略动作,当使用模式并省略动作时,默认动作为打印整行,所以,命令4表示打印所有行,因为每一行的模式都为真。

    命令5与命令6同理,在awk中,数字"0"与空字符串表示假,当模式为假时,不会执行对应的动作,而当存在模式并省略动作时,默认动作为打印整行,但是由于模式为假,所以对应的动作并未执行。

    其实,我们还能对真与假进行取反,非真即为假,非假即为真,示例如下。

    [root@server01 ~]# awk '0' test3
    [root@server01 ~]# awk '!0' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '5' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk '!5' test3
    [root@server01 ~]# 

    正如上例所示,我们使用"!"对真假进行取反。

    如果你已经看懂了上面的例子,那么,我们再来延伸一下。

    你猜猜,如下示例会输出什么?

    [root@server01 ~]# awk 'i=1' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk 'i=5' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk 'i="a"' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk 'i=0' test3
    [root@server01 ~]# awk 'i=" "' test3
    hey
    heey
    heeey
    heeeey
    [root@server01 ~]# awk 'i=""' test3
    [root@server01 ~]# 

    没错,聪明如你一定想到了,上例中,其实是使用了awk的变量,将变量 i  赋值为1,当 i=1 以后,i为非零值,表示为真,我们可以认为这是一种模式匹配后的结果,当模式为真时,同时省略了对应动作时,默认动作为打印整行,所以上例会输出test3中的所有行。

    理解完上述示例以后,我们再回过头来,看看之前打印奇数行的示例,你可能就会明白了。

    [root@server01 ~]# awk 'i=!i' test
    第 1 行
    第 3 行
    第 5 行
    第 7 行
    第 9 行
    第 11

    当awk开始处理第一行时,变量 i 被初始化,变量 i 在被初始化时,值为"空",而awk中,数字0或者"空字符串"表示假,所以可以认为模式为假,但是 i 直接取反了,对假取反后的值为真,将取反后的值又赋值给了变量i,此刻,变量i的值为真,所以当awk处理第一行文本时,变量i的值被赋值为真,模式成立则需要执行对应的动作,而上例中又省略了动作,所以默认动作为"{print $0}",所以,第一行被整行打印了。

    当第一行文本处理完毕后,awk开始处理第二行文本,此时,i 为真,但是取反后,i 为假,所以第二行没有被输出,依次类推,最终只打印了奇数行。

    为了能够更加直观的看到上述过程,我们将i的值打印出来,通过如下动作,能够打印出处理每一行时,i 对应的值。

    [root@server01 ~]# awk '{i=!i;print i}' test
    1
    0
    1
    0
    1
    0
    1
    0
    1
    0
    1

    当然,聪明如你,我就不用再解释打印偶数行的原理了,我想你应该已经能够举一反三了。

  • 相关阅读:
    APP设计资源
    browsersync实现网页实时刷新(修改LESS,JS,HTML时)
    Browsersync + Gulp.js
    用原生js对表格排序
    js深复制
    c++刷题(43/100)矩阵旋转打印
    将本地的mongodb迁移到阿里云
    c++刷题(39/100)笔试题3
    c++刷题(37/100)笔试题2
    c++刷题(33/100)笔试题1
  • 原文地址:https://www.cnblogs.com/liujunjun/p/14211201.html
Copyright © 2020-2023  润新知