当前位置 博文首页 > 一颗小陨石的博客:Linux——三、数据处理(正则、sed、awk)

    一颗小陨石的博客:Linux——三、数据处理(正则、sed、awk)

    作者:[db:作者] 时间:2021-09-16 13:40

    一、正则使用

    正则字符作用
    ^str查找在行首的字符串str
    str$查找在行尾的字符串str
    .除换行符\n外的任何单个字符
    \转义符
    *匹配前面的字符或子表达式 0-N次
    +重复前面的字符或子表达式 1-N次
    ?重复前面的字符或子表达式 0-1次
    [list]匹配中括号里的字符【注:q[wert] 表示的字符可以是qw、qe、qr、qt,即只要包含括号里的单个字符都匹配】
    [^list]与[list]相反,表示不包含括号里的字符
    str1|str2或,满足左右两个字符或两个表达式的字符串
    {n,m}1. {n}:匹配前面一个字符或子表达式n次【必须n次,不能多不能少】
    2.{n,}:匹配前面一个字符或子表达式至少n次
    3.{n,m}:匹配…n到m次
    ()标记一个子表达式的开始和结束
    [A-Z]和[a-z]分别表示所有大写字母和小写字母
    [0-9]所有数字
    \s匹配任何空白符,包括空格、制表符、换页符等
    \S任何非空白字符
    ?=**exp1(?=exp2):**匹配exp2表达式前面的exp1表达式匹配的字符
    ?<=(?<=exp2)exp1 匹配exp2表达式后面的exp1表达式所匹配的字符
    ?!exp1(?!exp2) 匹配后面不是exp2表达式的exp1
    ?<!(?<!exp2)exp1 匹配前面不是exp2的exp1

    示例文本:

    [root@localhost ~]# cat regex_txt
    lala
    haha
    heihei
    yoyo
    

    示例:

    1. 查找行首为l的:

      [root@localhost ~]# grep -n '^l' regex_txt
      1:lala
      
    2. 查找行尾为o的:

      [root@localhost ~]# grep -n 'o$' regex_txt
      4:yoyo
      
    3. 查找符合axa的字符,即两个a中间必须有一个字符的字符:

      [root@localhost ~]# grep -n 'a.a' regex_txt
      1:lala
      2:haha
      

    已练习,不想列了 太懒了

    二、sed工具

    sed也是管道命令,可处理标准输入。可对数据进行替换、删除、新增和选取特定行功能。

    语法:sed [选项] [操作]

    选项:

    -n: 只列出sed处理的那些行
    -f: 后跟filename,可将sed操作写入到文件,通过-f执行
    -r: 使用扩展正则语法(默认支持基础正则)
    -i: 直接修改读取的文件内容,非仅仅输出到屏幕
    

    操作:

    [n1,n2] oper

    n1和n2表示要操作的行数,oper为具体的操作行为,分为以下几点:

    • a

      新增,接新增的字符【新增到下一行】

    • c

      替换change。替换n1-n2间的行为指定内容

    • d

      删除。删除指定的[n1,n2]行

    • i

      插入。与a相同,但插入到当前的前一行

    • p

      打印。通常与 sed -n 搭配使用

    • s

      替换。可搭配正则

    示例:

    [root@localhost ~]# cat test.txt
    a
    b
    c
    d
    e
    f
    g
    h
    i
    j
    k
    l
    m
    n
    o
    p
    q
    

    有以上一个文本。

    2.1 删除

    删除指定行数

    # 删除/etc/passwd 3-6行
    [root@localhost ~]# cat -n test.txt | sed '3,6d'
         1	a
         2	b
         7	g
         8	h
         9	i
        10	j
        11	k
        12	l
        13	m
        14	n
        15	o
        16	p
        17	q
    # 删除3行以后
    [root@localhost ~]# cat -n test.txt | sed '3,$d'
         1	a
         2	b
    # 删除第3行
    [root@localhost ~]# cat -n test.txt | sed '3d'
         1	a
         2	b
         4	d
         5	e
         6	f
         7	g
         8	h
         9	i
        10	j
        11	k
        12	l
        13	m
        14	n
        15	o
        16	p
        17	q
    

    其中,3行以后用3,$d表示,$表示最后一行。

    2.2 新增

    分别在第二行后和第二行前加上 love u

    [root@localhost ~]# cat -n test.txt | sed '2a love u'
         1	a
         2	b
    love u
         3	c
         4	d
         5	e
         6	f
         7	g
         8	h
      [root@localhost ~]# cat -n test.txt | sed '2i love u'
         1	a
    love u
         2	b
         3	c
         4	d
         5	e
         6	f
         7	g
         8	h
         # 新增多行
       [root@localhost ~]# cat -n test.txt | sed '2i love u\
    > haha'
         1	a
    love u
    haha
         2	b
         3	c
         ....
    

    2.3 替换

    替换3到6行的内容为:love u

    [root@localhost ~]# cat -n test.txt | sed '3,6c love u'
         1	a
         2	b
    love u
         7	g
         8	h
         9	i
        10	j
        ....
    

    2.4 打印

    截取3,6行打印出来:

    [root@localhost ~]# cat -n test.txt | sed -n '3,6p'
         3	c
         4	d
         5	e
         6	f
    

    一定要加 -n 啊,不然就会额外打印出其他所有数据:

    [root@localhost ~]# cat -n test.txt | sed '3,6p'
         1	a
         2	b
         3	c
         3	c
         4	d
         4	d
         5	e
         5	e
         6	f
         6	f
         7	g
        .....
    

    2.5 直接替换指定字符

    刚刚的 c 操作是替换指定行,那如果我只想替换指定的字符怎么办?就可以用 s 操作,它更像是查询->替换。

    语法:

    `sed ‘s/替换的字符/新字符/g’,当然,替换的字符可以使用正则进行匹配

    将c替换为lala:

    [root@localhost ~]# cat -n test.txt | sed 's/c/lala/g'
         1	a
         2	b
         3	lala
         4	d
         5	e
         6	f
         7	g
         8	h
         9	i
        10	j
    

    2.6 直接修改原文件

    使用-i就可以直接修改原文件:

    a修改问heihei:

    [root@localhost ~]#  sed -i  's/a/heihei/g' test.txt
    [root@localhost ~]# cat test.txt
    heihei
    b
    c
    d
    e
    f
    g
    h
    

    三、awk

    awk倾向于将一行分成多个字段进行处理,一行内默认的字段分隔符为空格或TAB,适合处理小文本。

    awk '条件类型1{操作1} 条件类型2{操作2}' filename

    先看一个简单的例子:

    打印last中的第1列和第2列

    [root@localhost ~]# last | awk '{print $1 "\t" $2}'
    root	pts/0
    root	pts/0
    root	pts/0
    root	pts/1
    root	pts/0
    reboot	system
    root	tty1
    root	pts/1
    

    一下就出来了,而之前用cut命令,按照空格分割,会因无法确定有多少个空格而无法准确的取到第2列值。awk由于默认按照空格或TAB分割,自动将一行拆分成多个字段,我们想打印哪个字段,就直接通过print n 就 可 以 , n就可以, nn就代表第n个字段,n从1开始。$0表示一整行数据

    3.1 内置变量

    • NF

      每行的字段数

    • NR

      当前awk处理的第几行

    • FS

      当前输入字段分割字符,默认空格

    • RS

      输入记录分隔符 默认换行符,即每行作为一个单位进行处理,每行处理的分隔符为FS

    • OFS

      输出字段分隔符,默认空格

    • ORS

      输出记录分隔符,默认换行符

    [root@localhost ~]# cat test.txt
    heihei  hehe
    b wo ai ni
    c yo yo n n n
    d
    e
    f
    g
    

    以上文本。

    非变量要用双引号引起来

    [root@localhost ~]# cat test.txt | awk '{print "当前行:"NR",字段数:"NF",分隔符:"FS}'
    当前行:1,字段数:2,分隔符:
    当前行:2,字段数:4,分隔符:
    当前行:3,字段数:6,分隔符:
    当前行:4,字段数:1,分隔符:
    当前行:5,字段数:1,分隔符:
    当前行:6,字段数:1,分隔符:
    当前行:7,字段数:1,分隔符:
    

    3.2 逻辑运算

    包括:

    >
    <
    >=
    <=
    ==
    !=
    

    对/etc/passwd 按 冒号进行分割,找出第3列小于10的行,然后打印出第1和第3列:

    [root@localhost ~]# cat /etc/passwd | awk '{FS=":"} $3<10 {print $1 "\t" $3}'
    root:x:0:0:root:/root:/bin/bash
    bin	1
    daemon	2
    adm	3
    lp	4
    sync	5
    shutdown	6
    halt	7
    mail	8
    

    但是,这里虽然指定了分隔符,但是只能作用在第2行和以后,第一行还是默认空格分割,如果想要第一行也被作用,需要使用BEGIN关键字:

    [root@localhost ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3<10 {print $1 "\t" $3}'
    root	0
    bin	1
    daemon	2
    adm	3
    lp	4
    sync	5
    shutdown	6
    halt	7
    mail	8
    

    第一行就列出来了。

    同样还有END关键字

    • BEGIN

      awk会在读取任何输入行前执行BEGIN中指定的动作

    • END

      awk会在其退出前执行END指定的动作

      [root@localhost ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3>10 {print $1 "\t" $3} END {print $1}'
      operator	11
      games	12
      ftp	14
      nobody	99
      systemd-network	192
      dbus	81
      polkitd	999
      sshd	74
      postfix	89
      wangmaolin	1000
      wangmaolin
      

      在最后的时候打印出第一个变量

    示例:

    有如下文件:

    [root@localhost ~]# cat cal.txt
    col1 col2 col3
    1    2    3
    4    5   6
    7  8  9
    

    计算每列的和放到第4列:

    [root@localhost ~]cat cal.txt | awk 'NR==1 {printf "%5s %5s %5s %5s\n", $1,$2,$3,"sum"}\
    NR>=2 {printf "%5d %5d %5d %5d\n",$1,$2,$3,$1+$2+$3}'
     col1  col2  col3   sum
        1     2     3     6
        4     5     6    15
        7     8     9    24
    

    指定输出分隔符:

    [root@localhost ~]# cat cal.txt | awk 'BEGIN{OFS="---"} {print $1,$2,$3}'
    col1---col2---col3
    1---2---3
    4---5---6
    7---8---9
    

    3.3 数组和循环

    awk的数组看作python的字典,索引非索引,而是可理解为key,指定的key存放对应的value,key唯一,无序,使用for遍历时,是遍历的key,通过arr[key]可访问对应的值。

    [root@localhost ~]# awk 'BEGIN {arr[1]=2
    arr["a"] = 3
    arr["haha"] = "heihei"
    for(key in arr)
    {
    print arr[key]
    }
    }'
    #结果:
    3
    heihei
    2
    

    删除元素也和python一样,delete arr[key]

    [root@localhost ~]# awk 'BEGIN {arr[1]=2
    arr["a"] = 3
    arr["haha"] = "heihei"
    for(item in arr)
    {
    print arr[item]
    }
    delete arr["a"]
    print arr["a"]
    }'
    # 结果
    3
    heihei
    2
        # 打印空白,说明删除了
    

    3.4 通过文件调用awk 并传参

    其他的,awk也支持标准的流程运算,函数等,因此我们可以把它看作一个 脚本,也因此,我们可以把awk命令直接写入文件,通过调用文件的方式执行awk操作。

    awk -f awk脚本文件名 [赋值变量] 数据文件

    
    [root@localhost ~]# cat awk_script
    {print $n name}
    # 调用脚本,并给变量赋值
    [root@localhost ~]# awk -f awk_script n=2 name="test" cal.txt
    col2test
    2test
    5test
    8test
    

    更多用法:

    http://c.biancheng.net/view/4097.html

    cs