zoukankan      html  css  js  c++  java
  • Linux正则与文本处理工具

    该系列文章只是本人的学习笔记,文章中的文字描述提取自《Linux鸟哥私房菜》《Linux运维之道》等书中的重点内容,化繁为简能够在工作中快速复习掌握重点,并不代表个人立场,但转载请加出处,并注明参考文献。

    正则表达式 (Regular Expression, RE, 或称为常规表达式)是通过一些特殊字符的排列,用于『查找/替换/删除』一行或多行文字或字符串,简单的说,正则表达式就是用在字串的处理上面的一种『表示公式』,正则表达式并不是一个工具程序,而是一个对字符串处理的标准依据,如果您想要以正则表达式的方式处理字串,就得要使用支持正则表达式的工具程序才行,这类的工具程序很多,例如 vi,vim,sed,awk,gawk,egrep等.

    正则表达式,对于系统管理员来说是非常重要的,因为系统会产生很多的信息,这些信息有的重要有的仅是警告,此时管理员可以通过正则表达式来过滤出相应的我们需要的字段,你最好掌握这门技术,会对将来的数据分析,主机管理起到很大的帮助.

    基础正则表达式

    在上一章说过正则表达式和通配符的区别,(正则表达式用来在文件中匹配符合条件的字符串,而通配符则是用来匹配符合条件的文件名)吗? 其实这种区别只在Shell当中适用,因为用来在文件当中搜索字符串的命令,如 grep、awk、sed 等命令可以支持正则表达式,而在系统当中搜索文件的命令,如 ls、find、cp 这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了.

    首先我们先来学习一下基础的正则表达式吧,下面是我们常用的正则语法,我会给每一个语法,举一个小例子,下面我们开始学习吧.

    实例1: 使用 * 实现匹配前一个字符出现0次或任意多次(本例中,则是匹配g字符出现0次或任意多次)

    [root@localhost ~]# ls
    wang  wangg  wanggg
    
    [root@localhost ~]# ls | grep "wang*"
    wang
    wangg
    wanggg
    [root@localhost ~]# ls | grep "wan*g*"
    wang
    wangg
    wanggg
    

    实例2: 使用 . 实现匹配除换行符以外的任意一个字符,(只匹配一个字符),一般我们把它叫做贪婪匹配

    [root@localhost ~]# ls
    wang  wangg  wanggg wangr
    
    [root@localhost ~]# ls | grep "wang"
    wang
    [root@localhost ~]# ls | grep "wang."
    wangg
    wangr
    [root@localhost ~]# ls | grep "wang.."
    wanggg
    

    实例3: 使用 ^ 实现匹配行首是指定字符的行

    [root@localhost ~]# ls
    alert  lyshark  tcpl  wakaka  wang  wangg  wanggg  woxin
    
    [root@localhost ~]# ls | grep "^ly"
    lyshark
    [root@localhost ~]# ls | grep "^wa"
    wakaka
    wang
    wangg
    wanggg
    [root@localhost ~]# ls | grep "^a"
    alert
    

    实例4: 使用 $ 实现匹配行尾是指定字符的行

    [root@localhost ~]# ls
    alert  lyshark  tcpl  wakaka  wang  wangg  wanggg  woxin
    
    [root@localhost ~]# ls | grep "a$"
    wakaka
    [root@localhost ~]# ls | grep "ark$"
    lyshark
    [root@localhost ~]# ls | grep "^w" | grep "n$"     #匹配开头是w结尾是n的
    woxin
    

    实例5: 使用 [] 实现匹配中括号内任意一个字符,(只匹配其中一个)

    [root@localhost ~]# ls
    ale1t  ale2t  ale3t  aleat  alebt  alert
    
    [root@localhost ~]# ls | grep "ale[a-z]t"
    aleat
    alebt
    alert
    [root@localhost ~]# ls | grep "ale[0-9]t"
    ale1t
    ale2t
    ale3t
    [root@localhost ~]# ls | grep "ale[ab]t"
    aleat
    alebt
    

    实例6: 使用 [^] 实现匹配除了中括号字符以外的任意一个字符(^取反的意思)

    [root@localhost ~]# ls
    ale1t  ale2t  ale3t  aleat  aleAt  aleBB  alebt  aleCT  aleEt  alert
    
    [root@localhost ~]# ls | grep "ale[^0-9]t"
    aleat
    aleAt
    alebt
    aleEt
    alert
    [root@localhost ~]# ls | grep "ale[^A-Z]t"
    ale1t
    ale2t
    ale3t
    aleat
    alebt
    alert
    [root@localhost ~]# ls | grep "ale[^AE]t"
    ale1t
    ale2t
    ale3t
    aleat
    alebt
    alert
    
    
    

    实例7: 使用 ^[^] 实现匹配行首是与不是指定字符的行

    [root@localhost ~]# ls
    ale1t  ale2t lyshark  tcpl  wakaka  wang  wangg  wanggg  woxin
    
    [root@localhost ~]# ls | grep ^[a]        #匹配行首是a开头的
    ale1t
    ale2t
    [root@localhost ~]# ls | grep ^[^a]       #匹配行首不是a开头的
    lyshark
    tcpl
    wakaka
    wang
    wangg
    wanggg
    woxin
    

    实例8: 使用 {n} 实现匹配前面的字符恰好出现了n次的行

    [root@localhost ~]# ls
    12333  13466614578  13562653874  172.168.1.2  18264758942  192.168.1.1  45666  78999
    
    [root@localhost ~]# ls | grep "123{3}"
    12333
    [root@localhost ~]# ls | grep "[0-9][0-9][0-9]{3}"    #匹配前两个字符是0-9的,最后一个字符出现过3次的
    12333
    45666
    78999
    [root@localhost ~]# ls | grep "[1][3-8][0-9]{9}"       #匹配手机号规则
    13466614578
    13562653874
    18264758942
    

    实例9: {n,} 实现匹配前面的字符的出现,不小于n次的行

    [root@localhost ~]# ls
    12333  123333  1233333
    
    [root@localhost ~]# ls | grep "123{3,}"               #前一个字符3的出现不小于3次
    12333
    123333
    1233333
    [root@localhost ~]# ls | grep "123{4,}"               #前一个字符3的出现不小于4次
    123333
    1233333
    

    实例10: {n,m} 实现匹配前面的字符出现,不小于n次,最多出现m次的行

    [root@localhost ~]# ls
    123  1233  12333  123333  1233333  12333333  123333333  1233333333  12333333333
    
    [root@localhost ~]# ls |grep "123{3,5}"              #前一个字符3最少出现3次,最多不大于5次
    12333
    123333
    1233333
    

    总结:匹配一个合法IP地址

    [root@localhost ~]# ls
    10.10.10.22  127.9.0.8    172.168.1.2  192.168.1.1  192.168.1.3      255.255.255.255
    127.0.0.1    172.168.1.1  172.168.1.3  192.168.1.2  192.199.256.256  256.256.256.256
    
    [root@localhost ~]# ls | egrep "^(([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|
    2[0-4][0-9]|25[0-4])$"
    
    10.10.10.22
    172.168.1.1
    172.168.1.2
    172.168.1.3
    192.168.1.1
    192.168.1.2
    192.168.1.3
    

    总结:匹配一个合法邮箱地址

    [root@localhost ~]# ls
    1181506874@qq.com  lysharks@163.cn  lysharks@163.com  wangrui@126.com  wangrui@129.cc
    
    [root@localhost ~]# ls | egrep "^[0-9a-zA-Z][0-9a-zA-Z_]{1,16}[0-9a-zA-Z]@[0-9a-zA-Z-]*([0-9a-zA-Z])?.(com|com.cn|net|
    org|cn)$"
    
    1181506874@qq.com
    lysharks@163.cn
    lysharks@163.com
    wangrui@126.com
    

    扩展正则表达式

    事实上,一般用户只要了解基础型的正则语句就已经相当足够了,不过,在某些时候,为了简化命令的长度,还是需要扩展正则的支持的,打个比方,我们可以使用 cat xxx.log | grep -v '^$' |grep -v '^#' 来实现过滤文本中的注释行,和空白行,但是这不够精简,它的执行还是会用到两次的过滤,如果使用扩展正则表达式,我们可以这样写,cat xxx.log |egrep -v '^$|^#' 这样一个命令就可实现上面的效果啦,此处我们需要说明的是,grep -E 和egrep效果是相同的,使用哪一个都一个样.

    熟悉了基础正规表达式之后,再来看这个扩展正则表达式,是不是很轻松啊,亲,下面我们就来分别说明这几个符号的使用规则吧.

    实例1: + 实现匹配前一个字符出现1次或任意多次

    [root@localhost ~]# ls
    gogle  google  gooogle  gooogooogle  goooogle  gooooogle  goooooogle
    
    [root@localhost ~]# ls | grep -E "go+gle"
    gogle
    google
    gooogle
    goooogle
    gooooogle
    goooooogle
    

    实例2: ? 实现匹配前一个字符出现0次,或1次

    [root@localhost ~]# ls
    gogle  google  gooogle  gooogooogle  goooogle  gooooogle  goooooogle
    
    [root@localhost ~]# ls | grep -E "go?gle"
    gogle
    [root@localhost ~]# ls | grep -E "goo?gle"
    gogle
    google
    

    实例3: | 实现匹配两个或多个分支选择

    [root@localhost ~]# ls
    alert  lyshark  rui  wang
    
    [root@localhost ~]# ls | grep -E "alert|lyshark"
    alert
    lyshark
    [root@localhost ~]# ls | grep -E "wang|rui|alert"
    alert
    rui
    wang
    

    实例4: () 实现将字符作为一个整体匹配,即模式单元

    [root@localhost ~]# ls
    dog  dogdog  dogdogdog  hello_lyshark  hello_world
    
    [root@localhost ~]# ls | grep -E "(dog)+"
    dog
    dogdog
    dogdogdog
    [root@localhost ~]# ls | grep -E "hello_(world|lyshark)"
    hello_lyshark
    hello_world
    

    Grep 行处理工具

    grep (global search regular expression(RE) and print out the line 全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来,好了废话不多说,先看命令格式.

    [root@localhost ~]# grep --help
    
    命令语法:[ grep [选项] [过滤菜单] 文件名 ]
    
            -a              #将二进制数据一同列出
            -c              #计算找到查找字符串的次数
            -i              #忽略大小写差异
            -n              #顺便标号显示
            -v              #反选参数
            -q              #不显示任何提示信息,安静模式
            -E              #使用扩展正则,egrep
            -A              #匹配指定字符的后n个字符
            -B              #匹配指定字符的前n个字符
            -C              #匹配指定字符的前n个和后n个字符
    

    实例1: 使用 grep -n 参数过滤数据时,一同标号

    [root@localhost ~]# cat /etc/passwd | grep -n "root"
    
    1:root:x:0:0:root:/root:/bin/bash
    10:operator:x:11:0:operator:/root:/sbin/nologin
    

    实例2: 使用 grep -v 反选打印,行中不包括/bin/bash的行

    [root@localhost ~]# cat /etc/passwd | grep -vn "/bin/bash"
    
    2:bin:x:1:1:bin:/bin:/sbin/nologin
    3:daemon:x:2:2:daemon:/sbin:/sbin/nologin
    4:adm:x:3:4:adm:/var/adm:/sbin/nologin
    5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    6:sync:x:5:0:sync:/sbin:/bin/sync
    7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    8:halt:x:7:0:halt:/sbin:/sbin/halt
    ....省略....
    

    实例3: 使用 grep -i 过滤出不论大小写的lyshark单词的行

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    GOOD
    
    [root@localhost ~]# cat lyshark.log | grep -ni "lyshark"
    1:LyShark
    2:lyshark
    

    实例4: 使用正则,过滤出开头是 ^L 的行的内容

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    GOOD
    
    [root@localhost ~]# cat lyshark.log | grep -n "^L"
    1:LyShark
    

    实例5: 使用正则,过滤出开头不是 ^L的行的内容

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    GOOD
    
    [root@localhost ~]# cat lyshark.log | grep -n "[^L]yshark"
    2:lyshark
    

    实例6: 使用正则,过滤出开头是小写字母的行

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    GOOD
    123123
    1233
    66431
    124adb
    
    [root@localhost ~]# cat lyshark.log | grep -n "^[a-z]"
    2:lyshark
    3:admin
    

    实例7: 使用正则,过滤出开头不是,a-z或A-Z的行

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    GOOD
    123123
    1233
    66431
    124adb
    
    [root@localhost ~]# cat lyshark.log | grep -n "^[^a-zA-Z]"
    8:123123
    9:1233
    10:66431
    11:124adb
    

    实例8: 使用正则,找出结尾是小数点的哪一行

    [root@localhost ~]# cat lyshark.log
    LyShark
    lyshark
    admin
    Admin
    ADMIN
    Good
    123123
    1233.
    66431.
    
    [root@localhost ~]# cat lyshark.log |grep -n ".$"
    8:1233.
    9:66431.
    

    实例9: 使用正则,过滤掉开头是#号的,和开头是空行的行

    [root@localhost ~]# cat lyshark.log
    #LyShark
    #lyshark
    #admin
    #Admin
    
    ADMIN
    Good
    
    123123
    
    1233.
    66431.
    
    [root@localhost ~]# cat lyshark.log | grep -v "^#" | grep -v "^$"
    ADMIN
    Good
    123123
    1233.
    66431.
    

    实例10: 使用正则,过滤出前一个字符o刚好出现两次的行

    [root@localhost ~]# cat lyshark.log
    #LyShark
    #lyshark
    #admin
    #Admin
    
    ADMIN
    Good
    123123
    1233.
    66431.
    
    [root@localhost ~]# cat lyshark.log |grep -n "o{2}"
    7:Good
    

    实例11: 使用正则,匹配开头是0-9且结尾是点的行

    [root@localhost ~]# cat lyshark.log
    #LyShark
    #lyshark
    #admin
    #Admin
    
    ADMIN
    Good
    123123
    1233.
    66431.
    
    [root@localhost ~]# cat lyshark.log |grep -n -E "^[0-9]+."
    9:1233.
    10:66431.
    

    实例12: 使用正则,匹配指定字符的后2个字符,或前2个字符

    [root@localhost ~]# cat lyshark.log
    lyshark
    LySHARK
    wang
    rui
    hello
    world
    alert
    123123
    45678
    
    [root@localhost ~]# cat lyshark.log |grep -n -A 2 "hello"
    5:hello
    6-world
    7-alert
    
    [root@localhost ~]# cat lyshark.log |grep -n -B 2 "hello"
    3-wang
    4-rui
    5:hello
    

    Cut 列提取工具

    cut命令用来显示行中的指定部分,删除文件中指定字段,cut经常用来显示文件的内容,类似于type命令.该命令有两项功能,其一是用来显示文件的内容,它依次读取由参数file所指明的文件,将它们的内容输出到标准输出上.其二是连接两个或多个文件,如cut fl f2 > f3 将把文件fl和f2的内容合并,然后通过输出重定向符">" 的作用,将它们放入文件f3中.

    首先我们来看一下它的格式吧,如下:

    [root@localhost ~]# cut --help
    
    命令语法:[ cut [选项] [列号] 文件名 ]
    
            -f              #-f 列号:指定提取第几列
            -d              #-d 分隔符:按照指定分隔符进行分割
            -c              #-c 字符范围:不依赖分割符来分割,而是通过字符范围进行字段提取
            -m              #表示从第一个字符提取到第m个
            -b              #仅显示行中指定直接范围的内容
            -n              #与"-b"选项连用,不分割多字节字符
            n-              #表示从第n个字符开始提取到结尾
            n-m             #表示从第n提取到第m个字符
    
            --complement    #补齐被选择的字节,字符或字段
            --out-delimiter=<字段分隔符> #指定输出内容是的字段分割符
    

    手动创建一个文本,添加内容列之间用tab分隔,用来测试后续内容

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    

    实例1: 通过使用 -f 选项指定过滤的列,并显示到屏幕

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    
    [root@localhost ~]# cut -f 2 lyshark.log
    NAME
    WR
    LC
    LY
    
    [root@localhost ~]# cut -f 2,5 lyshark.log
    NAME    Mark
    WR      100
    LC      90
    LY      88
    

    实例2: 通过使用 --complement 选项提取指定字段之外的列,(打印除了第2列之外的列)

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    
    [root@localhost ~]# cut -f 2 --complement lyshark.log
    
    ID      AGE     Gender  Mark
    1       22      m       100
    2       26      m       90
    3       23      m       88
    

    实例3: 通过使用 -c 选项过滤/etc/passwd,并打印第1个到第3个字符

    [root@localhost ~]# cut -c 1-3 /etc/passwd
    roo
    bin
    dae
    adm
    lp:
    syn
    ....省略....
    

    实例4: 通过使用 -c -2 选项过滤/etc/passwd,并打印前2个字符

    [root@localhost ~]# cut -c -2 /etc/passwd
    ro
    bi
    da
    ad
    lp
    ....省略....
    

    实例5: 通过使用 -c 5- 选项过滤/etc/passwd,打印从第5个字符开始到结尾

    [root@localhost ~]# cut -c 5- /etc/passwd
    :x:0:0:root:/root:/bin/bash
    x:1:1:bin:/bin:/sbin/nologin
    on:x:2:2:daemon:/sbin:/sbin/nologin
    x:3:4:adm:/var/adm:/sbin/nologin
    ....省略....
    

    实例6: 通过使用 -d 指定分隔符 -f 指定打印第个字段,以下我们分别截取第1和第7个字段

    [root@localhost ~]# cut -d ":" -f 1,7 /etc/passwd
    root:/bin/bash
    bin:/sbin/nologin
    daemon:/sbin/nologin
    adm:/sbin/nologin
    lp:/sbin/nologin
    sync:/bin/sync
    ....省略....
    

    实例7: 通过使用 -c -3 指定截取前3个字符,还可以通过 -c 3 截取第3个字符

    [root@localhost ~]# cut -c -3 /etc/passwd
    roo
    bin
    dae
    adm
    lp:
    ....省略....
    [root@localhost ~]# cut -c 3 /etc/passwd
    o
    n
    e
    m
    :
    n
    ....省略....
    

    Sed 流编辑器

    sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用,功能不同凡响.处理时,把当前处理的行存储在临时缓冲区中,称为 "模式空间" (pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复,直到文件末尾.文件内容并没有改变,除非你使用重定向存储输出.Sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转换程序等.

    sed主要是来进行数据选取,替换,删除,新增的命令,二话不说先看一下它的参数吧.

    [root@localhost ~]# sed --help
    
    命令语法:[ sed [选项] [范围] [动作] 文件名 ]
    
            -n              #把经过sed命令处理的行输出到屏幕
            -e              #允许对输入数据应用多条sed命令编辑
            -f              #从sed脚本中读入sed操作,和awk命令的-f类似
            -r              #在sed中支持扩展正则表达式
            -i              #用sed的修改结果,写到文件
    
    命令动作:
    
            p               #打印,输出指定的行
            a               #追加,在当前行后添加一行或多行
            i               #插入,在当前行前插入一行或多行
            c               #整行替换,用c后面的字符串替换原数据行
            d               #删除,删除指定的行
            s               #字串替换,格式:"行范围s/旧字串/新字串/g"
    
    #对sed命令我们要知道的是,它所有的修改都不会直接修改文件的内容,而是在内存中进行处理然后打印到屏幕上
    #如果想要写入文件,请使用 sed -i 选项才会保存到文本中.
    

    在进行实验之前,首先创建一个文件,来做测试用

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例1: 使用 sed '2p' 重复打印第二行数据

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '2p' lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    1       WR      22      m       100    ←本行是2p打印的
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '3p' lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    2       LC      26      m       90     ←本行是2p打印的
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例2: 使用 sed -n 限定,只选取指定的行进行显示

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed -n '2p' lyshark.log  ←只打印第2行数据
    1       WR      22      m       100
    
    [root@localhost ~]# sed -n '1p' lyshark.log  ←只打印第1行数据
    ID      NAME    AGE     Gender  Mark
    

    实例3: 使用 sed '2,4d' 删除掉文件2-4行,并显示到屏幕,(原文件内容并没有被修改)

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '2,4d' lyshark.log   ←删除2-4行的数据并打印
    ID      NAME    AGE     Gender  Mark
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '1d' lyshark.log     ←删除第1行的数据
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例4: 使用 sed '2[a|i]' 追加,或者插入数据指定数据

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '2a hello lyshark' lyshark.log      ←在第2行后面追加数据
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    hello lyshark
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '2i hello lyshark' lyshark.log      ←在第2行前面插入数据
    ID      NAME    AGE     Gender  Mark
    hello lyshark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例5: 使用 换行符,一次插入多行数据

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed '2a hello     ←在第二行下面,插入一段话,用隔开
    > my name is lyshark 
    > age 22 
    > boy ' lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    hello
    my name is lyshark
    age 22
    boy
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例6: 使用 sed 'c' 实现整行替换数据

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# cat lyshark.log | sed '5c 5    WRS    99    m    111'  ←整行替换第5行内容
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5    WRS    99    m    111
    

    实例7: 使用 sed 后面跟上 -i 选项,将第5行的修改,保存进文件,(-i选项是回写)

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# sed  -i '5c 5     WRS    99    m    111' lyshark.log
    
    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5       WRS    99    m    111               ←这里的数据已经写入成功
    

    实例8: 字符串的替换 sed 's/旧文本/新文本/g' 进行整行替换

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5       WRS     99      m       111
    
    [root@localhost ~]# sed '2s/WR/LyShark/g' lyshark.log   ←将第2行的,WR替换成LyShark
    ID      NAME    AGE     Gender  Mark
    1       LyShark 22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5       WRS     99      m       111
    
    [root@localhost ~]# sed '3s/LC/Admin/g' lyshark.log     ←将第3行的,LC替换成Admin
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       Admin   26      m       90
    3       LY      23      m       88
    5     WRS    99    m    111
    
    #注意:上方只是替换打印,并没有保存,如若想保存请加 -i 属性
    

    实例9: 将第3行数据的开头添加#注释

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5       WRS     99      m       111
    
    [root@localhost ~]# sed '3s/^/#/g' lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    #2      LC      26      m       90
    3       LY      23      m       88
    5       WRS     99      m       111
    

    实例10: 将第4和第5行的内容替换成空

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    5       WRS     99      m       111
    
    [root@localhost ~]# sed -e '4s/LY//g ; 5s/WRS//g' lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3               23      m       88
    5               99      m       111
    
    #sed 要进行多行操作时,只能通过 -e 写多条操作语句,用 ; 或回车分隔
    

    过滤IP地址小实验

    [root@localhost ~]# ifconfig
    ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.1.10  netmask 255.255.255.0  broadcast 192.168.1.255
            inet6 fe80::897c:d72d:cd95:b9ec  prefixlen 64  scopeid 0x20<link>
            ether 00:0c:29:b1:b7:be  txqueuelen 1000  (Ethernet)
            RX packets 2344  bytes 156370 (152.7 KiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 454  bytes 50049 (48.8 KiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    [root@localhost ~]# ifconfig | grep "inet 192" |sed 's/^.*inet //g'
    192.168.1.10  netmask 255.255.255.0  broadcast 192.168.1.255
    
    [root@localhost ~]# ifconfig |grep "inet 192" |sed 's/^.*inet //g' |sed 's/ netmask.*$//g'
    192.168.1.10
    

    Printf 文本格式化

    [root@localhost ~]# printf --help
    
    输出类型:
            %ns:            #输出字符串,n是数字,指代输出几个字符
            %ni:            #输出证书,n是数字,指代输出几个数字
            %m.nf:          #输出浮点数,m和n是数字,指代输出整数位和小数位
    
    输出格式:
            a:             #输出警告音
            :             #输出退格键,也就是Backspace键
            f:             #清屏
            
    :             #换行符
            
    :             #回车,也就是Enter键
            	:             #水平输出退格键,也就是Tab键
            v:             #垂直输出退格键,也就是Tab键
    
    #注意:print 和 printf 的主要区别在有 printf 是标准的格式化输出,必须手动指定换行和tab.
    

    在进行实验之前,首先创建一个文件,来做测试用

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    实例1: 通过 printf 函数打印文本内容

    printf命令,如果不指定输出格式,则会把所有输出内容连在一起输出,其实文本的输出本身就是这样的,cat等文本输出命令之所以可以按照格式漂亮的输出,那是因为cat命令已经设定了输出格式.

    [root@localhost ~]# printf '%s' $(cat lyshark.log)
    
    IDNAMEAGEGenderMark1WR22m1002LC26m903LY23m884XDL40b100
    

    实例2: 通过 printf 格式化后输出一段文本

    [root@localhost ~]# printf '%s	 %s	 %s	 %s	 %s	 
    ' $(cat lyshark.log)
    
    ID       NAME    AGE     Gender  Mark
    1        WR      22      m       100
    2        LC      26      m       90
    3        LY      23      m       88
    4        XDL     40      b       100
    

    实例3: 通过 printf 按照整数型和浮点型输出,则需要修改

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# printf '%i	 %s	 %8.2f	 %s	 %s	 
    ' $(cat lyshark.log |grep -v ID)
    1        WR         22.00        m       100
    2        LC         26.00        m       90
    3        LY         23.00        m       88
    4        XDL        40.00        b       100
    

    ## Awk 正则表达工具

    awk是一种编程语言,用于在linux/unix下对文本和数据进行处理,数据可以来自标准输入(stdin),一个或多个文件,或其它命令的输出.它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具,它在命令行中使用,但更多是作为脚本来使用.awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势.

    在开始看例子之前,老样子,你懂的

    [root@localhost ~]# awk --help
    Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
    Usage: awk [POSIX or GNU style options] [--] 'program' file ...
    
    命令语法:[ awk '条件1{动作1} 条件2{动作2} ....' 文件名 ]
    
    条件:一般使用关系表达式作为条件
                    x>10    判断x变量是否大于10
                    x==y    判断变量x是否等于变量y
                    A ~ B   判断字符串A中是否包含能匹配B表达式的字符串
                    A!~ B   判断字符串A中是否不包含能匹配B表达式的字符串
    

    在进行实验之前,首先创建一个文件,来做测试用

    [root@localhost ~]# cat lyshark.log
    
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    

    ◆awk常用例子◆

    实例1: 使用awk格式化输出第二列和第三列的内容

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# awk '{ printf $2 "	" $3 "
    " }' lyshark.log
    NAME    AGE
    WR      22
    LC      26
    LY      23
    XDL     40
    

    实例2: 通过awk命令截取Size和Used命令的显示列

    [root@localhost ~]# df -h
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root  8.0G  1.2G  6.9G  15% /
    devtmpfs                  98M     0   98M   0% /dev
    tmpfs                    110M     0  110M   0% /dev/shm
    tmpfs                    110M  5.5M  104M   5% /run
    tmpfs                    110M     0  110M   0% /sys/fs/cgroup
    /dev/sda1               1014M  130M  885M  13% /boot
    tmpfs                     22M     0   22M   0% /run/user/0
    
    [root@localhost ~]# df -h |awk '{ printf $2 "	" $3 "
    " }'
    Size    Used
    8.0G    1.2G
    98M     0
    110M    0
    110M    5.5M
    110M    0
    1014M   130M
    22M     0
    

    ◆begin 与 end◆

    BEGIN是awk的保留字,是一种特殊的条件类型.BEGIN的执行时机是 "在awk程序一开始时,尚未读取任何数据之前执行",一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,所以BEGIN定义的动作只能被执行一次,例如:

    下面的,整个动作定义了两个动作,先打印 "执行语句前,执行我" 然后输出过滤后的2和4列

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# awk 'BEGIN{ printf "执行语句前,执行我 
    " }{ printf $2 "	" $4 "
    " }' lyshark.log
    执行语句前,执行我
    NAME    Gender
    WR      m
    LC      m
    LY      m
    XDL     b
    

    END也是awk保留字,不过刚好和BEGIN相反,END是在awk程序处理完所有数据,即将结束时执行.END后的动作只在程序结束时执行一次.

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# awk 'END{ printf "执行语句结束后,执行我 
    " }{ printf $2 "	" $4 "
    " }' lyshark.log
    NAME    Gender
    WR      m
    LC      m
    LY      m
    XDL     b
    执行语句结束后,执行我
    

    BEGIN与END连用,以下例子也就是实现了,开始前执行打印 "执行语句前,先执行我",结束时执行打印 "执行语句后,在执行我"

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# awk 'BEGIN{ printf "执行语句前,先执行我 
    " }END{ printf "执行语句后,在执行我 
    "}{ printf $2 "	"
    $4 "
    " }' lyshark.log
    
    执行语句前,先执行我
    NAME    Gender
    WR      m
    LC      m
    LY      m
    XDL     b
    执行语句后,在执行我
    

    ◆awk关系运算◆

    关系运算符:设定条件,符合执行不符合不执行,下面设定条件为AGE字段>=25岁的列出

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# cat lyshark.log | grep -v ID | awk '$3 >=25 {print $1 "	" $2}'
    2       LC
    4       XDL
    

    ◆awk正则搜索◆

    awk是列提取命令,第一步的动作却是先读入第一行,整个执行步骤:

    ⦁ 如果有BEGIN条件,则先执行BEGIN定义动作.
    ⦁ 如果没有BEGIN条件,则先读入第一行,把第一行的数据依次赋成$0 $1 $2 $3 …等变量,$0 代表整行数据,$1 则为第一个字段,依次类推.
    ⦁ 读入下一行,重复赋值变量,并打印数据.

    指定搜索: 正则搜索第2列NAME字段,包含XDL的行,并打印出AGE列对应的值

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# awk '$2 ~/XDL/ {printf $3 "
    "}' lyshark.log
    40
    
    [root@localhost ~]# df -h
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root  8.0G  1.2G  6.9G  15% /
    devtmpfs                  98M     0   98M   0% /dev
    tmpfs                    110M     0  110M   0% /dev/shm
    tmpfs                    110M  5.5M  104M   5% /run
    tmpfs                    110M     0  110M   0% /sys/fs/cgroup
    /dev/sda1               1014M  130M  885M  13% /boot
    tmpfs                     22M     0   22M   0% /run/user/0
    
    [root@localhost ~]# df -h | grep -v "Filesystem" | awk ' $1 ~/sda1/ {print $2}'
    1014M
    

    全局搜索: 正则全局搜索包含WR的字段行,并打印本行

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     Gender  Mark
    1       WR      22      m       100
    2       LC      26      m       90
    3       LY      23      m       88
    4       XDL     40      b       100
    
    [root@localhost ~]# cat lyshark.log | awk '/WR/ {printf $0 "
    "}'
    1       WR      22      m       100
    

    屏蔽显示: 查看 df 的真实分区的使用情况,不看光盘和虚拟磁盘

    [root@localhost ~]# df -h
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root  8.0G  1.2G  6.9G  15% /
    devtmpfs                  98M     0   98M   0% /dev
    tmpfs                    110M     0  110M   0% /dev/shm
    tmpfs                    110M  5.5M  104M   5% /run
    tmpfs                    110M     0  110M   0% /sys/fs/cgroup
    /dev/sda1               1014M  130M  885M  13% /boot
    tmpfs                     22M     0   22M   0% /run/user/0
    
    [root@localhost ~]# df -h | awk '/sda[0-9]/ {printf $1 "	" $5"
    "}'
    /dev/sda1       13%
    

    ◆awk内置变量◆

    实例1: 通过内置变量 FS=":" 定义分隔符,打印/etc/passwd文件的第1列和第3列

    [root@localhost ~]# cat /etc/passwd |grep "/bin/bash"
    root:x:0:0:root:/root:/bin/bash
    
    [root@localhost ~]# cat /etc/passwd |grep "/bin/bash" | 
    > awk 'BEGIN {FS=":"} {printf $1 "	" $3 "
    "}'
    
    root    0
    

    实例2: 打印行内容的同时,打印出行号(NR变量),和字段数(NF变量)

    [root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | 
     awk 'BEGIN{FS=":"} {print $1 "	" $3 "	" "行号: " NR "	" "
    字段数: " NF}'
    
    root    0       行号: 1 字段数: 7
    

    实例3: 打印行内容,首先判断 $1==sshd 然后再打印本行的行号等信息

    [root@localhost ~]# cat /etc/passwd | grep "/sbin/nologin" | 
     awk 'BEGIN{FS=":"}$1=="sshd" {print $1 "	" $3 "	" "行号
    : " NR "	" "字段数: " NF}'
    
    sshd    74      行号: 13        字段数: 7
    

    实例4: 一个分区统计的小例子

    [root@localhost ~]# df -h |grep -v "Filesystem" | 
    awk '$1=="/dev/sda1" {print $1 "	" $5 "	" "行号:" NR "	" "字段数:" NF }'
    
    /dev/sda1       13%     行号:6  字段数:6
    
    

    ◆awk流程控制◆

    在awk编程中,因为命令语句非常长,输入格式时需要注意以下内容:

    ⦁ 多个条件{动作}可以用空格分割,也可以用回车分割.
    ⦁ 在一个动作中,如果需要执行多个命令,需要用";"分割,或用回车分割.
    ⦁ 在awk中,变量的赋值与调用都不需要加入"$"符.
    ⦁ 条件中判断两个值是否相同,请使用"==",以便和变量赋值进行区分.

    实例1: 将第2行,到第4行,的内容加起来,最后输出结果

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     SEX     LINUX
    1       LY      18      boy     100
    2       SC      30      man     150
    3       WR      22      man     90
    4       ZSX     55      boy     96
    
    [root@localhost ~]# awk 'NR==2{x=$3} NR==3{y=$3} NR==4{z=$3} {totle=x+y+z;print "Totle is:" totle}' lyshark.log
    Totle is:0
    Totle is:18
    Totle is:48
    Totle is:70
    Totle is:70
    
    [root@localhost ~]# awk 'NR==2{x=$3} NR==3{y=$3} NR==4{z=$3;totle=x+y+z;print "Totle is:" totle}' lyshark.log
    Totle is:70
    

    实例2: 统计AGE列,将年龄小于25岁的任过滤出来,并显示 is young man!

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     SEX     LINUX
    1       LY      18      boy     10
    2       SC      30      man     50
    3       WR      22      man     90
    4       ZSX     55      boy     96
    
    [root@localhost ~]# cat lyshark.log | awk '{if (NR >= 2){if ($3 < 25) printf $2 " IS Young Man! 
    "}}'
    LY IS Young Man!
    WR IS Young Man!
    

    实例3: 统计LINUX列,当出现大于80分的,打印到屏幕上 is good man!

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     SEX     LINUX
    1       LY      18      boy     10
    2       SC      30      man     50
    3       WR      22      man     90
    4       ZSX     55      boy     96
    
    [root@localhost ~]# cat lyshark.log | awk 'NR>=2 {temp=$5} temp>80 {printf $2 "IS Good Man!
    "}'
    WRIS Good Man!
    ZSXIS Good Man!
    

    ◆awk函数编写◆

    实例1: 通过定义的函数格式,去匹配并传递参数

    [root@localhost ~]# cat lyshark.log
    ID      NAME    AGE     SEX     LINUX
    1       LY      18      boy     10
    2       SC      30      man     50
    3       WR      22      man     90
    4       ZSX     55      boy     96
    
    [root@localhost ~]# cat lyshark.log | awk 'function lyshark(x,y) {printf x "	" y "
    "}{lyshark($2,$5)}'
    NAME    LINUX
    LY      10
    SC      50
    WR      90
    ZSX     96
    

    实例2: awk中调用脚本,对于小的单行程序来说,将脚本作为命令行自变量传递给awk是非常简单的,而对于多行程序就比较难处理.当程序是多行的时候,使用外部脚本是很适合的.首先在外部文件中写好脚本,然后可以使用awk的-f选项,使其读入脚本并且执.

    [root@localhost ~]# cat passwd.awk
    BEGIN {FS=":"}
    {print $1 "	" $3}
    
    [root@localhost ~]# cat /etc/passwd | awk -f passwd.awk
    root    0
    bin     1
    daemon  2
    adm     3
    lp      4
    sync    5
    shutdown        6
    halt    7
    mail    8
    operator        11
    games   12
    ftp     14
    nobody  99
    systemd-network 192
    dbus    81
    polkitd 999
    sshd    74
    postfix 89
    chrony  998
    

    ## diff/patch 文件比对

    什么时候会用到文件的比对啊? 通常是『同一个套装软件的不同版本之间,比较配置文件的差异』,很多时候所谓的文件比对,通常是用在 ASCII 纯文字档的比对上的,那么比对文件最常见的就是 diff .

    diff命令在最简单的情况下,比较给定的两个文件的不同.如果使用 "-" 代替 "文件" 参数,则要比较的内容将来自标准输入,diff命令是以逐行的方式,比较文本文件的异同处,如果该命令指定进行目录的比较,则将会比较该目录中具有相同文件名的文件,而不会对其子目录文件进行任何比较操作.

    ◆diff 生成补丁文件◆

    [root@localhost ~]# diff --help
    Usage: diff [OPTION]... FILES
    Compare FILES line by line.
    
    语法格式:[ diff [选项] 源文件 新文件 > *.patch ]
    
            -a          #将任何文档当做文本文档处理
            -b          #忽略空格造成的不同
            -B          #忽略空白行造成的不同
            -I          #忽略大小写造成的不同
            -N          #当比较目录时,若某个文件只在一个目录中,则另一个目录中视作空文件
            -r          #当比较目录时,递归比较子目录
            -u          #使用同一的输出格式
    
    -----------------------------------------------------------------
    说明: 生成补丁的例子
    
    1.首先创建两个文件,分别写入以下内容,内容要不同,因为要生成补丁.
    
    [root@localhost ~]# cat old
    hello world
    
    [root@localhost ~]# cat new
    hello world
    
    welcome to lyshark blog
    
    2.利用命令生成补丁文件 *.patch
    
    [root@localhost ~]# diff -Naur /root/old /root/new > lyshark.patch
    
    [root@localhost ~]# cat lyshark.patch
    --- /root/old   2018-09-21 05:41:55.487052312 -0400
    +++ /root/new   2018-09-21 05:42:10.581184526 -0400
    @@ -1 +1,3 @@
     hello world
    +
    +welcome to lyshark blog
    
    #最后,生成的lyshark.patch就是补丁文件了,我们可以使用下面的命令对old文件打补丁了.
    

    ◆patch 文本打入补丁◆

    patch命令被用于为开放源代码软件安装补丁程序,让用户利用设置修补文件的方式,修改更新原始文件.如果一次仅修改一个文件,可直接在命令列中下达指令依序执行,如果配合修补文件的方式则能一次修补大批文件,这也是Linux系统核心的升级方法之一.

    注意:精简模式下没有这个命令,需要执行 yum install -y patch 安装一下

    [root@localhost ~]# patch --help
    Usage: patch [OPTION]... [ORIGFILE [PATCHFILE]]
    
    语法格式:[ patch [-pn] 旧文件 < *.patch ]
    
            -p:设置要剥离的目录层数   
            n:代表补丁文件中记录的文件old所在目录层数,用于更新old文件时匹配正确路径
    
    -----------------------------------------------------------------
    说明: 给old文件打补丁
    
    [root@localhost ~]# ls
    lyshark.patch  new  old
    
    [root@localhost ~]# patch -p2 old < lyshark.patch
    patching file old
    
    [root@localhost ~]# cat old
    hello world
    
    welcome to lyshark blog
    
    [root@localhost ~]# cat new
    hello world
    
    welcome to lyshark blog
    

    参考文献:Linux鸟哥私房菜,Linux运维之道

  • 相关阅读:
    《你必须知道的495个C语言问题》读书笔记之第15-20章:浮点数、风格、杂项
    《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
    《你必须知道的495个C语言问题》读书笔记之第4-7章:指针
    《你必须知道的495个C语言问题》读书笔记之第3章:表达式
    《你必须知道的495个C语言问题》读书笔记之第1-2章:声明和初始化
    bzoj4361 isn(树状数组优化dp+容斥)
    bzoj4665 小w的喜糖(dp+容斥)
    P4859 已经没有什么好害怕的了(dp+二项式反演)
    bzoj4710 [Jsoi2011]分特产(容斥)
    bzoj2839 集合计数(容斥)
  • 原文地址:https://www.cnblogs.com/LyShark/p/10221806.html
Copyright © 2011-2022 走看看