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运维之道

  • 相关阅读:
    nginx-1.8.1的安装
    ElasticSearch 在3节点集群的启动
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    sqoop导入导出对mysql再带数据库test能跑通用户自己建立的数据库则不行
    LeetCode 501. Find Mode in Binary Search Tree (找到二叉搜索树的众数)
    LeetCode 437. Path Sum III (路径之和之三)
    LeetCode 404. Sum of Left Leaves (左子叶之和)
    LeetCode 257. Binary Tree Paths (二叉树路径)
    LeetCode Questions List (LeetCode 问题列表)- Java Solutions
    LeetCode 561. Array Partition I (数组分隔之一)
  • 原文地址:https://www.cnblogs.com/LyShark/p/10221806.html
Copyright © 2011-2022 走看看