zoukankan      html  css  js  c++  java
  • linux三剑客

    awk是由Alfred Aho 、Peter Weinberger 和 Brian Kernighan这三个人创造的,awk由这个三个人的姓氏的首个字母组成。awk其实可以看成是一门独立的编程语言,他支持条件判断、数组、循环等功能。awk共有两个版本(gawk与nawk版本),在linux中我们最常使用的是gawk,同时awk、grep、sed被称为Linux中的三剑客

    [root@CodeSheep ~]# ll /usr/bin/awk 
    lrwxrwxrwx. 1 root root 4 Feb 15  2019 /usr/bin/awk -> gawk
    

    关于"三剑客"的特长

    • grep 更适合单纯的查找和匹配文本
    • sed 更适合编辑匹配的文本
    • awk 更适合格式化文本,对文本进行较复杂格式处理

    awk基础

    语法
    awk [options] '{pattern + action}' {filenames}
    其中的action 我们最常用的就是print以及prinf,对于action来说,每次经过一行,都会当前行执行一边action
    比如

    awk '{print "1",NR}' /etc/passwd 
    

    你会发现有多少行,他就输出了多少个1 每行一个

    awk的工作流程

    首先awk并不是列操作,而是行操作,同样的他也是一行一行的处理的,其中$0表示当前行,比如在正常情况下我们输出全文是用cat来查看的,那么用awk的操作是这样的,另外awk的接受标准输入和文件

    cat /etc/passwd
    awk '{print $0}' /etc/passwd
    
    # 如上的内容是一样的
    

    awk中的分隔符

    为了处理好每一行中的每一个字段,awk引入了分隔符的概念,分隔符有三种表现的形式,
    一种是不输入任何不指定任何,则默认为空格
    一种是自定义的分隔符 使用 -F 指定 比如 -F:
    另外一种是使用 内置变量指定 -v FS='#'

    自定义分隔符
    既然默认的分隔符是空格,那么当然还有自定义的分隔符咯。我们使用-F选项来指定我们的分隔符

    # 使用#分隔符
    [root@CodeSheep ~]# cat demo3.text 
    123#123#dsdshj#dlsj#
    [root@CodeSheep ~]# awk -F# '{print $1}' demo3.text 
    123
    [root@CodeSheep ~]# cat demo3.text 
    123#123#dsdshj#dlsj#
    [root@CodeSheep ~]# awk -v FS='#' '{print $1}' demo3.text 
    123
    

    其实在awk中不仅仅有输入的分割符,还有输出的分隔符,默认也为空格

    [root@CodeSheep ~]# awk -F# '{print $1,$2}' demo3.text 
    123 123
    

    OFS(output field separator)可以指定输出的分隔符,用法与FS相同

    [root@CodeSheep ~]# awk -v  FS='#' -v OFS='++++' '{print $1,$2}' demo3.text 
    123++++123
    

    awk的内建变量

    awk支持内建变量和自定义变量

    内建变量

    • $0 当前行
    • $1~$n 第n个字段
    • $NF 最后一个字段
    • NF 当前行被分割字段总数
    • NR 当前行行号
    • FNR 各文件分别计数的行号
    • FS 输入分隔符
    • OFS 输出分隔符
    • RS 输入换行符
    • ORS 输出换行符
    • FILENAME 当前文件名
    • ARGC 命令行参数的个数
    • ARGV 数组,保存的命令行所给定的各参数
    # 给每一行添加行号,输出当前行号,以及当前行的内容
    [root@CodeSheep ~]# df | awk '{print NR,$0}'
    1 Filesystem     1K-blocks    Used Available Use% Mounted on
    2 /dev/vda1       41147472 6848400  32395580  18% /
    3 devtmpfs          930656       0    930656   0% /dev
    4 tmpfs             941116       0    941116   0% /dev/shm
    5 tmpfs             941116     452    940664   1% /run
    6 tmpfs             941116       0    941116   0% /sys/fs/cgroup
    7 tmpfs             188224       0    188224   0% /run/user/0
    # 多个文件使用NR时,会根据的文件顺序累加序号
    # FNR则会分开标序
    
    # ARGV,ARGC
    [root@CodeSheep ~]# awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGC}' demo1.txt demo2.text 
    awk demo1.txt demo2.text 3
    # 其中ARGV作为数组,第一个参数是awk
    

    自定义变量名
    自定义变量的两种形式,一种是在action外面 使用选项指定变量,比如 -v name="hzj"
    另外一种则是在action前的pattern中定义,比如 {name="hzj"; action->print name}

    [root@CodeSheep ~]# awk -v name="hzj" 'BEGIN{print name}'
    hzj
    [root@CodeSheep ~]# awk 'BEGIN{name="hzj";print name}'
    hzj
    [root@CodeSheep ~]# name=hzj
    [root@CodeSheep ~]# awk -v name=$name 'BEGIN{print name}'
    hzj
    

    awk 参数

    options

    -v name="xx" -v外部定义变量

    pattern+action

    抛开Pattern 我们先来使用{action}

    # 输出文本内容
    [root@CodeSheep ~]# df
    Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/vda1       41147472 6848500  32395480  18% /
    devtmpfs          930656       0    930656   0% /dev
    tmpfs             941116       0    941116   0% /dev/shm
    tmpfs             941116     452    940664   1% /run
    tmpfs             941116       0    941116   0% /sys/fs/cgroup
    tmpfs             188224       0    188224   0% /run/user/0
    [root@CodeSheep ~]# df | awk '{print $1}'
    Filesystem
    /dev/vda1
    devtmpfs
    tmpfs
    tmpfs
    tmpfs
    tmpfs
    

    awk是逐行处理的,也就是说当awk处理一个文本的时候,他会一行一行的处理内容。其中awk默认以 换行符 为标记识别每一行。另外对于每一行的处理中 awk会按照用户指定的 分隔符 去分隔当前行,如果没有指定,则默认使用空格作为分隔符,当出现多个空格的时候,awk会自动将连续的空格理解成为一个分隔符。
    其中 我们将被awk处理后的每一行都使用了特定的变量标记
    $0表示当前处理的整行
    $1表示第一个字段
    $2表示第二个字段
    $NF表示最后一个字段
    NF表示当前行被分割后字段总数
    因此 假设一行文本被空格分为8段,则$NF表示$8 NF=8 则$7=$(NF-1)

    # 输出多行
    [root@CodeSheep ~]# df | awk '{print $1 $2 $3}'
    Filesystem1K-blocksUsed
    /dev/vda1411474726848536
    devtmpfs9306560
    tmpfs9411160
    tmpfs941116452
    tmpfs9411160
    tmpfs1882240
    [root@CodeSheep ~]# df | awk '{print $1, $2 ,$3}'
    Filesystem 1K-blocks Used
    /dev/vda1 41147472 6848536
    devtmpfs 930656 0
    tmpfs 941116 0
    tmpfs 941116 452
    tmpfs 941116 0
    tmpfs 188224 0
    # 自己添加字段
    [root@CodeSheep ~]# df | awk '{print $1 ,"this print test"}'
    Filesystem this print test
    /dev/vda1 this print test
    devtmpfs this print test
    tmpfs this print test
    tmpfs this print test
    tmpfs this print test
    tmpfs this print test
    [root@CodeSheep ~]# df | awk '{print "$1="$1 , "testfield:""this print test"}'
    $1=Filesystem testfield:this print test
    $1=/dev/vda1 testfield:this print test
    $1=devtmpfs testfield:this print test
    $1=tmpfs testfield:this print test
    $1=tmpfs testfield:this print test
    $1=tmpfs testfield:this print test
    $1=tmpfs testfield:this print test
    #看上面的案例我们可以发现,当$1被双引号包裹的时候,他就是一个字符串,不再是变量
    

    awk中的Pattern
    其实action的主要操作就是print输出,简单来用就是输出内容,更复杂的就是对输出的内容进行格式化的操作。而Pattern所存在的两种模式,愈加加强了awk的能力。

    awk中的逻辑

    为了更好的格式化,awk中也带有逻辑参数,其中包括开始BEGIN和结尾END,他们之间用{}分隔,比如

    awk -v test="test" 'BEGIN{print "1",NR}{print test}END{print "input end" }' /etc/passwd
    

    你会发现他输出了很多个test,首先begin进入,然后输出1和行号0 紧接着不断的进入行输出test 在最后一行输入时 执行input end

    特殊模式下的BEGIN与END

    • BEGIN 模式指定了处理文本之前需要执行的操作
    • END 模式指定了处理完所有行之后所需要执行的操作
    [root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"}'
    $1 $2
    [root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2}'
    $1 $2
    Filesystem 1K-blocks
    /dev/vda1 41147472
    devtmpfs 930656
    tmpfs 941116
    tmpfs 941116
    tmpfs 941116
    tmpfs 188224
    [root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2} END{print "end for file"}'
    $1 $2
    Filesystem 1K-blocks
    /dev/vda1 41147472
    devtmpfs 930656
    tmpfs 941116
    tmpfs 941116
    tmpfs 941116
    tmpfs 188224
    end for file
    

    在BEGIN的模式下,首先awk会执行BEGIN中的action 之后才做去执行其他的action,同理,在执行完所有的action之后,awk回去执行END模式下面的action。需要注意的是两个特殊的模式BEGIN以及END都需要大写

    于是 awk的格式化能力表露无疑了,提取字段再加上BEGIN与END的两种特殊模式,一张完整的表不就出来了嘛

    关系运算符

    <   小于      x < y
    <=  小于等于  x <= y
    ==  等于      x==y
    !=  不等于  x!=y
    > = 大于等于 x>=y
    >   大于  x>y
    ~  正则匹配  x ~ /正则/
    !~ 正则不匹配  x !~ /正则/
    

    awk中的正则模式

    既然上面的关系运算符中出现了正则,那我们就来讲一讲正则模式
    需要注意的是 当使用{x,y}这种次数匹配的正则表达式时,需要配合--posix选项或者--re-interval选项

    # 匹配/etc/passwd 中以 tss开头的行
    [root@CodeSheep ~]# cat /etc/passwd |  awk '/tss/{print $0}'
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
    # 匹配/etc/passwd 中以 /bin/bash结尾的行
    [root@CodeSheep ~]# cat /etc/passwd |  awk '//bin/bash$/{print $0}'
    root:x:0:0:root:/root:/bin/bash
    # 匹配/etc/passwd 中以/bin/bash结尾的行 到以 tss开头的行
    [root@CodeSheep ~]# cat /etc/passwd |  awk '//bin/bash$/,/^tss/{print $0}'
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    halt:x:7:0:halt:/sbin:/sbin/halt
    mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    operator:x:11:0:operator:/root:/sbin/nologin
    games:x:12:100:games:/usr/games:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    nobody:x:99:99:Nobody:/:/sbin/nologin
    systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
    dbus:x:81:81:System message bus:/:/sbin/nologin
    polkitd:x:999:998:User for polkitd:/:/sbin/nologin
    sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
    postfix:x:89:89::/var/spool/postfix:/sbin/nologin
    chrony:x:998:996::/var/lib/chrony:/sbin/nologin
    ntp:x:38:38::/etc/ntp:/sbin/nologin
    tcpdump:x:72:72::/:/sbin/nologin
    nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
    mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
    dockerroot:x:997:994:Docker User:/var/lib/docker:/sbin/nologin
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
    # 匹配行号大于2 且小于6的行
    [root@CodeSheep ~]# cat /etc/passwd |  awk 'NR>2 && NR<=6{print $0}'
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    

    awk中的语法逻辑

    在awk中,同样可以使用if for while等在编程语法中出现的逻辑判断
    if(){语句1;语句2;}

    # 输出df第一行
    [root@CodeSheep ~]# df | awk 'NR==1{print $0}'
    Filesystem     1K-blocks    Used Available Use% Mounted on
    [root@CodeSheep ~]# df | awk '{if(NR==1){print $0}}'
    Filesystem     1K-blocks    Used Available Use% Mounted on
    

    if(){语句1;语句2;}else{语句3;语句4;}
    if(){语句1;语句2;}else if(){语句3;语句4;}else{语句5;语句6;}


    for(初始化;表达式;更新){语句}
    for(变量 in 数组){语句}
    while(表达式){语句}
    do{语句}while(条件)

    # 循环输出1-10
    [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){print i}}'
    1
    2
    ...
    [root@CodeSheep ~]# awk 'BEGIN{i=1;while(i<=10){print i;i++}}'
    1
    2
    3
    ...
    # 使用continue和break
    [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){continue};print i}}'
    1
    2
    4
    ...
    [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){break};print i}}'
    1
    2
    # 使用exit退出当前脚本
    [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){break};print i}}'
    1
    2
    [root@CodeSheep ~]# df | awk 'BEGIN{print "filed"}{exit}{print $1}END{print "end file"}'
    filed
    end file
    # 并不是直接结束,而是跳到了end
    # 使用next 直接跳到下一行
    df | awk 'BEGIN{print "输出第一行的下一行"}{if(NR==1){next};print $0}'
    [root@CodeSheep ~]# df | awk 'BEGIN{print "输出第一行的下一行"}{if(NR==1){next};print $0;{exit}}'
    输出第一行的下一行
    /dev/vda1       41147472 6851112  32392868  18% /
    

    sed基础

    sed [参数][定址commands][inputfile]

    参数:
    --version 查看版本
    --help 查看帮助
    -n 关闭默认输出,默认自动打印所有行
    -e 多点编辑,允许多个脚本指令被执行
    -r 支持正则
    -i 修改原文件内容
    -f 支持使用脚本文件

    命令:
    /p 打印变动行
    s/ 替换
    y/ 替换
    /n 替换第几个匹配的内容
    /g 匹配所有行
    /d 删除当前行
    i 从当前行前插入
    a 从当前行后插入
    c 替换匹配行

    使用案例

    输出

    输出passwd文件的1~3行
    sed -n '1,3p' /etc/passwd
    输出passwd文件的root~daemon行
    sed -n '/^root/,/daemon/p'
    输出passwd文件中uid是0或1的行
    sed -n '/x:[0:1]:/p' /etc/passwd
    

    删除

    删除第一行
    sed '1d' /etc/passwd 
    删除第一行和第三行
    sed -e '1d' -e '3d' /etc/passwd
    sed  '1d;3d' /etc/passwd
    保留第一行 删除其他行
    sed '1!d' 
    

    替换

    匹配第一个并修改
    sed 's/root/ROOT' /etc/passwd
    匹配第二个并修改
    sed 's/root/ROOT' /etc/passwd
    匹配所有并修改
    sed 's/root/ROOT/g' /etc/passwd
    匹配修改成功后,打印变动行
    sed -n 's/root/ROOT/pg' /etc/passwd
    修改后另存为其他文件
    sed -n 's/root/ROOT/pg;w passwd.md' /etc/passwd
    sed -n 's/root/ROOT/pgw passwd.md' /etc/passwd
    在文件的每行前面添加#注释
    sed -n 's/^/#/p' /etc/passwd
    删除文件的第1个字符
    sed 's/^.//1' /etc/passwd
    删除文件的第二个字符
    sed 's/.//2' /etc/passwd
    在每一行的上面添加hello 
    sed 'i hello' /etc/passwd
    在第一行的上面添加hello
    sed '1a hello' /etc/passwd
    将a~z替换成A~Z 
    sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' /etc/passwd
    替换匹配行
    sed -n '1c hello' /etc/passwd
    输出1行
    sed -n '1q' /etc/passwd
    打印奇数行
    sed -n 'p;n' /etc/passwd
    打印偶数行
    sed -n 'n;p' /etc/passwd
    打印匹配字符串的下一行
    sed -n '/^root/{n;p}' /etc/passwd 
    

    grep基础

    grep (global regular Expression Print) 全局正则表达式输出

    grep (选项) pattern flie
    
    -A <显示行数> 除了显示符合匹配行,也显示匹配行后的n行
    -B <显示行数> 除了显示符合匹配行,也显示匹配行前的n行
    -C <显示行数>  A+B  显示前后n行
    -c 统计匹配行数的总数,仅显示
    -e 实现多个选项件的逻辑关系 or
    -E 扩展的正则表达式
    -f FILE:从FILE获取PATTERN匹配
    -i --ignore-case 忽略字符大小写的差别
    -n 显示匹配的行号
    -o 仅显示匹配到的字符串
    -v 反向匹配,显示不被pattern匹配到的行
    -w 匹配整个单词  
    

    需要注意的地方

    1. egrep = grep -E 除了 < >  以外其他正则都可以去掉
    2. 如果有要匹配的内容中,有( ; )这种的话最好加"" 比如 grep ";" test.txt

    正则表达式

    匹配内容
    .匹配全部字符,不匹配空行
    []匹配指定范围内的任意单个字符
    [^] 取反
    [:alnum:] [0-9a-zA-Z]
    [:alpha:] [a-zA-Z]
    [:upper:] 或 [A-Z]
    [:lower:] 或 [a-z]
    [:blank:] 匹配空白字符
    [:punct:] 标点符号
    匹配次数

    *匹配任意次 0-n次 ,贪婪模式(尽可能多匹配)
    .*匹配任意次 1-n次 
    ?匹配 0-1次
    +至少匹配1次
    char{n} 匹配char n次
    char{n,m} 匹配char n-m次
    char{,n} 匹配char 最多n次
    char{n,} 匹配char 至少n次
    

    位置确定
    ^ 行首
    $ 行尾
    ^$ 空白行

    划组匹配
    在正则表达示中使用之前匹配到的内容,即划组匹配

    # 分组形式 ()
    # 读取方式 1 2 3
    

  • 相关阅读:
    【hdu 4135】Co-prime
    【cdoj 1544】当咸鱼也要按照基本法
    【SRM 717 DIV2 C】DerangementsDiv2
    【codeforces 821E】Okabe and El Psy Kongroo
    【SRM 717 div2 B】LexmaxReplace
    【SRM 717 div2 A】 NiceTable
    Network architecture for minimalistic connected objects
    C# 委托的理解
    50条超精辟的经典语录:哗众,可以取宠,也可以失宠!
    50条超精辟的经典语录:哗众,可以取宠,也可以失宠!
  • 原文地址:https://www.cnblogs.com/Alpacapyer/p/11880543.html
Copyright © 2011-2022 走看看