zoukankan      html  css  js  c++  java
  • 006 Shell第二篇:正则表达式和文本处理工具

    Shell第二篇:正则表达式和文本处理工具

    一 什么是正则

      正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。

    生活中处处都是正则:

        比如我们描述:4条腿

          你可能会想到的是四条腿的动物或者桌子,椅子等

        继续描述:4条腿,活的

              就只剩下四条腿的动物这一类了

    在linux中,通配符是由shell解释的,而正则表达式则是由命令解释的,下面我们就为大家介绍三种文本处理工具/命令:grep、sed、awk,它们三者均可以解释正则。

    二 grep

    参数

    -n  :显示行号
    -o  :只显示匹配的内容
    -q  :静默模式,没有任何输出,得用$?来判断执行成功没有,即有没有过滤到想要的内容

    -l  :如果匹配成功,则只将文件名打印出来,失败则不打印,通常-rl一起用,grep -rl 'root' /etc 
    -A  :如果匹配成功,则将匹配行及其后n行一起打印出来
    -B  :如果匹配成功,则将匹配行及其前n行一起打印出来
    -C  :如果匹配成功,则将匹配行及其前后n行一起打印出来
    --color
    -c  :如果匹配成功,则将匹配到的行数打印出来
    -E  :等于egrep,扩展
    -i  :忽略大小写

    -v  :取反,不匹配
    -w:匹配单词

     
    [root@MiWiFi-R3-srv ~]# cat a.txt 
    root123
    ROot asdf
    Root_123
    rOOtss
    root 123
    [root@MiWiFi-R3-srv ~]# grep -i "root" a.txt 
    root123
    ROot asdf
    Root_123
    rOOtss
    root 123
    [root@MiWiFi-R3-srv ~]# grep -w "root" a.txt 
    root 123
     

    grep种类
    grep
    fgrep
    pgrep
    egrep

    正则介绍

    ^ 行首
    $ 行尾
    . 除了换行符以外的任意单个字符
    * 前导字符的零个或多个
    .* 所有字符
    [] 字符组内的任一字符
    [^] 对字符组内的每个字符取反(不匹配字符组内的每个字符)
    ^[^] 非字符组内的字符开头的行
    [a-z] 小写字母
    [A-Z] 大写字母
    [a-Z] 小写和大写字母
    [0-9] 数字
    < 单词头 单词一般以空格或特殊字符做分隔,连续的字符串被当做单词
    > 单词尾

    扩展正则 sed 加 -r 参数 或转义
    grep 加 -E 或 egrep 或转义
    AWK 直接支持 但不包含{n,m}
    可以使用--posix支持
    [root@MiWiFi-R3-srv ~]#  awk '/ro{1,3}/{print}' /etc/passwd
    [root@MiWiFi-R3-srv ~]#  awk --posix '/ro{1,3}/{print}' /etc/passwd

    sed -n '/roo?/p' /etc/passwd
    sed -rn '/roo?/p' /etc/passwd
    ? 前导字符零个或一个
    + 前导字符一个或多个
    abc|def abc或def
    a(bc|de)f abcf 或 adef
    x{m} x出现m次
    x{m,} x出现m次至多次(至少m次)
    x{m,n} x出现m次至n次

    posix定义的字符分类

    [:alnum:] Alphanumeric characters.
    匹配范围为 [a-zA-Z0-9]
    [:alpha:] Alphabetic characters.
    匹配范围为 [a-zA-Z]
    [:blank:] Space or tab characters.
    匹配范围为 空格和TAB键
    [:cntrl:] Control characters.
    匹配控制键 例如 ^M 要按 ctrl+v 再按回车 才能输出
    [:digit:] Numeric characters.
    匹配所有数字 [0-9]
    [:graph:] Characters that are both printable and visible. (A space is print-
    able, but not visible, while an a is both.)
    匹配所有可见字符 但不包含空格和TAB 就是你在文本文档中按键盘上能用眼睛观察到的所有符号
    [:lower:] Lower-case alphabetic characters.
    小写 [a-z]
    [:print:] Printable characters (characters that are not control characters.)
    匹配所有可见字符 包括空格和TAB
    能打印到纸上的所有符号
    [:punct:] Punctuation characters (characters that are not letter, digits, con-
    trol characters, or space characters).
    特殊输入符号 +-=)(*&^%$#@!~`|"'{}[]:;?/>.<,
    注意它不包含空格和TAB
    这个集合不等于^[a-zA-Z0-9]
    [:space:] Space characters (such as space, tab, and formfeed, to name a few).

    [:upper:] Upper-case alphabetic characters.
    大写 [A-Z]
    [:xdigit:] Characters that are hexadecimal digits.
    16进制数 [0-f]

    使用方法:
    [root@seker ~]# grep --color '[[:alnum:]]' /etc/passwd

    正则表达式及字符处理
    目标文件/etc/passwd,使用grep命令或egrep
    1.显示出所有含有root的行:
    2.输出任何包含bash的所有行,还要输出紧接着这行的上下各两行的内容:
    3.  显示出有多少行含有nologin。
    4.显示出那些行含有root,并将行号一块输出。
    5.显示出文件中
    6.新建用户
        abominable
        abominate
        anomie
        atomize
        编写正则表达式,将他们匹配出来
        egrep 'a.omi(nabl|nat|z|)e' /etc/passwd
    7.建四个用户
        Alex213sb
        Wpq2222b
        yH438PIG
        egon666
        egon
    
        过滤出用户名组成是字母+数字+字母的行
    [root@MiWiFi-R3-srv ~]# egrep '^[a-Z]+[0-9]+[a-Z]+' /etc/passwd
    8.显示出/etc目录下所有包含root的文件名
    9. 过滤掉/etc/ssh/sshd_config内所有注释和所有空行
    grep -v '^#' /etc/ssh/sshd_config |grep -v '^ *$'
    grep作业

    三 sed

    sed
    流编辑器 stream editer,是以行为单位的处理程序

    sed 流编辑器 stream editer

    语法
    sed [options] 'command' in_file[s]
    options 部分
    -n
    -e
    -i
    -f
    command 部分
    '[地址1,地址2] [函数] [参数(标记)]'

    定址的方法 1.数字 2.正则
    数字
    十进制数
    1 单行
    1,3 范围 从第一行到第三行
    2,+4 匹配行后若干行
    4,~3 从第四行到下一个3的倍数行
    2~3 第二行起每间隔三行的行
    $ 尾行
    1! 除了第一行以外的行
    正则
    正则必须用//包裹起来
    扩展正则需要用 -r 参数或转义

    数字定址:sed -n '1p' /etc/passwd

    正则定址:sed -n '/^root/p' /etc/passwd

    正则介绍

    ^ 行首
    $ 行尾
    . 除了换行符以外的任意单个字符
    * 前导字符的零个或多个
    .* 所有字符
    [] 字符组内的任一字符
    [^] 对字符组内的每个字符取反(不匹配字符组内的每个字符)
    ^[^] 非字符组内的字符开头的行
    [a-z] 小写字母
    [A-Z] 大写字母
    [a-Z] 小写和大写字母
    [0-9] 数字
    < 单词头 单词一般以空格或特殊字符做分隔,连续的字符串被当做单词
    > 单词尾

    扩展正则 加 -r 参数 或转义
    sed -n '/roo?/p' /etc/passwd
    sed -rn '/roo?/p' /etc/passwd
    ? 前导字符零个或一个
    + 前导字符一个或多个
    abc|def abc或def
    a(bc|de)f abcf 或 adef
    x{m} x出现m次
    x{m,} x出现m次至多次(至少m次)
    x{m,n} x出现m次至n次


    函数
    增删改
    a 后插
    c 替换
    i 前插
    d 删除
    输入输出
    p 打印匹配的行 一般和 -n 参数连用,以屏蔽默认输出
    r 从文件中读入
    w 写入到文件中
    控制流
    ! 命令取反 例: 1!d 删除第一行以外的行
    {} 命令组合 命令用分号分隔 {1h;G} 可以理解为 -e 参数的另一种写法

    = 打印行号(输入行的号码,而非处理的次数行号) 例如: sed -n '2{=;p}' infile
    n 读入下一行到模式空间 例:'4{n;d}' 删除第5行
    N 而是追加下一行到模式空间,再把当前行和下一行同时应用后面的命令

    替换
    s 字符串替换 s/old/new/

    $ sed -n 's/root/ABCDEF/p' /etc/passwd

    ABCDEF:x:0:0:root:/root:/bin/bash

    operator:x:11:0:operator:/ABCDEF:/sbin/nologin

    $ sed -n 's/root/ABCDEF/gp' /etc/passwd

    ABCDEF:x:0:0:ABCDEF:/ABCDEF:/bin/bash

    operator:x:11:0:operator:/ABCDEF:/sbin/nologin

    $ sed -n 's/root/ABCDEF/2p' /etc/passwd

    root:x:0:0:ABCDEF:/root:/bin/bash

    $ sed -n 's/root/ABCDEF/3p' /etc/passwd

    root:x:0:0:root:/ABCDEF:/bin/bash

    $ sed -n 's/root/ABCDEF/gp' /etc/passwd

    ABCDEF:x:0:0:ABCDEF:/ABCDEF:/bin/bash

    operator:x:11:0:operator:/ABCDEF:/sbin/nologin

    $

    () 保存被匹配的字符 以备反向引用N时使用 最多9个标签 标签顺序从左至右
    & 替换时使用,在不定义标签时使用(反向引用)


    试做:

    删除第一个单词

    删除最后一个单词

    将第一个单词和最后一个单词兑换位置


    y 字符替换(变形)

    工作模式 模式空间和保持空间介绍


    $ sed '1{p;p}' a.txt

    11111111

    11111111

    11111111

    22222222

    33333333

    44444444

    55555555

    66666666

    $


    置换 模式空间和保持空间(暂存空间)
    h 把模式空间内容覆盖到保持空间中
    H 把模式空间内容追加到保持空间中
    g 把保持空间内容覆盖到模式空间中
    G 把保持空间内容追加到模式空间中
    x 交换模式空间与保持空间的内容

    # cat test.sh
    1111111
    2222222
    3333333
    4444444
    # sed '{1h;2,3H;4G}' ./test.sh
    1111111
    2222222
    3333333
    4444444
    1111111
    2222222
    3333333
    # sed '{1h;2x;3g;$G}' ./test.sh
    1111111
    1111111
    2222222
    4444444
    2222222
    #


    试做题

    将第一行插入到每个偶数行的后面

    $ sed '1h;0~2G' a.txt

    11111111

    22222222

    11111111

    33333333

    44444444

    11111111

    55555555

    66666666

    11111111

    $

    颠倒输出

    $ sed '1!G;h;$!d' rev.txt

    xyz

    def

    abc

    $

    脚本方法
    -f 参数 引用脚本(脚本的末尾不能有空格制表符或其他文本)
    # cat sed.sh
    2,4d
    s/777/seker/
    s/999/seker&seker/
    # sed -f sed.sh test.txt
    1111111
    5555555
    6666666
    seker7777
    8888888
    seker999seker9999
    #

    在脚本中指明解释器为sed
    # cat sed.sh
    #!/bin/sed -f
    2,4d
    s/777/seker/
    s/999/seker&seker/
    # ./sed.sh test.txt
    1111111
    5555555
    6666666
    seker7777
    8888888
    seker999seker9999
    #

    高级流控命令 b分支 t测试
    分支命令用于无条件转移,测试命令用于有条件转移

    分支 branch
    跳转的位置与标签相关联
    如果有标签则跳转到标签所在的后面行继续执行
    如果没有标签则跳转到脚本的结尾处.
    标签 以冒号开始后接标签名 不要在标签名前后使用空格
    跳转到标签指定位置
    [root@stu254 ~]# grep seker /etc/passwd
    seker:x:500:500::/home/seker:/bin/bash
    [root@stu254 ~]#
    [root@stu254 ~]# grep seker /etc/passwd |sed ':top;s/seker/blues/;/seker/b top;s/5/555/'
    blues:x:55500:500::/home/blues:/bin/bash
    [root@stu254 ~]#

    命令分析:让单次替换(cmd1)循环执行,直到条件不满足
    :top; 定义一个top标签
    s/seker/blues/; cmd1
    /seker/b top; 如果模式匹配则跳转到top标签
    s/5/555/ 当上一条模式不匹配时,既会继续执行这一条

    选择执行
    [root@stu254 ~]# grep 'seker' /etc/passwd |sed 's/seker/blues/;/seker/b end;s/5/555/;:end;s/5/666/'
    blues:x:66600:500::/home/seker:/bin/bash
    [root@stu254 ~]#

    zorro:x:501:501::/home/zorro:/bin/bash
    [root@stu254 ~]# grep 'zorro' /etc/passwd |sed 's/seker/blues/;/seker/b end;s/5/555/;:end;s/5/666/'
    zorro:x:6665501:501::/home/zorro:/bin/bash
    [root@stu254 ~]#

    命令分析: 执行cmd1,再去模式匹配,成功则跳转到cmd3开始执行,否则(模式不匹配)会按命令顺序逐个执行
    s/seker/blues/; cmd1
    /seker/b end;
    s/5/555/; cmd2
    :end;
    s/5/666/ cmd3

    另一种选择执行
    [root@stu254 ~]# grep 'seker' /etc/passwd |sed 's/seker/blues/;/seker/b end;s/5/555/;b;:end;s/5/666/'
    blues:x:66600:500::/home/seker:/bin/bash

    [root@stu254 ~]# grep 'zorro' /etc/passwd |sed 's/seker/blues/;/seker/b end;s/5/555/;b;:end;s/5/666/'
    zorro:x:55501:501::/home/zorro:/bin/bash
    [root@stu254 ~]#

    命令分析: 执行cmd1;模式匹配cmd2成功则执行cmd3;否则执行cmd2,再跳转到脚本末尾
    s/seker/blues/; cmd1
    /seker/b end;
    s/5/555/; cmd2
    b;
    :end;
    s/5/666/ cmd3


    测试命令,如果前一个替换命令执行成功则跳转到脚本末尾 (case结构)
    [root@stu254 ~]# grep 'seker' /etc/passwd |sed 's/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ/'
    ABC:x:500:500::/home/seker:/bin/bash

    [root@stu254 ~]# grep 'zorro' /etc/passwd |sed 's/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ/'
    zorro:x:501:501::/DEF/zorro:/bin/bash
    [root@stu254 ~]#

    与标签关联,跳转到标签位置
    [root@stu254 ~]# grep 'seker' /etc/passwd |sed 's/seker/ABC/;t end;s/home/DEF/;t;:end;s/bash/XYZ/'
    ABC:x:500:500::/home/seker:/bin/XYZ
    [root@stu254 ~]#

    [seker@seker ~]$ grep 'zorro' /etc/passwd |sed 's/seker/ABC/;t end;s/home/DEF/;t;:end;s/bash/XYZ/'
    zorro:x:501:501::/DEF/zorro:/bin/bash

    Sed作业:以/etc/passwd文件为模板
    
    1,删除文件每行的第一个字符。
    
    2,删除文件每行的第二个字符。
    
    3,删除文件每行的最后一个字符。
    
    4,删除文件每行的倒数第二个字符。
    
    5,删除文件每行的第二个单词。
    
    6,删除文件每行的倒数第二个单词。
    
    7,删除文件每行的最后一个单词。
    
    8,交换每行的第一个字符和第二个字符。
    
    9,交换每行的第一个字符和第二个单词。
    
    10,交换每行的第一个单词和最后一个单词。
    
    11,删除一个文件中所有的数字。
    
    12,删除每行开头的所有空格。
    
    13,用制表符替换文件中出现的所有空格。
    
    14,把所有大写字母用括号()括起来。
    
    15,打印每行3次。
    
    16,隔行删除。
    
    17,把文件从第2行到第5行复制到第7行后面。(选做题)
    
    18,把文件从第2行到第5行移动到第7行后面。(选做题)
    
    19,只显示每行的第一个单词。
    
    20,打印每行的第一个单词和第三个单词。
    
    21,将格式为    mm/yy/dd    的日期格式换成   mm;yy;dd
    
    22, a.txt内容
        ABC
        DEF
        XYZ
        通过SED实现tac命令
        tac a.txt
        XYZ
        DEF
        ABC
    
    1. sed -r 's/^(.)(.*)/2/' /etc/passwd
    2. sed -r 's/^(.)(.)(.*)/13/' /etc/passwd
    3. sed -r 's/(.*)(.)$/1/' /etc/passwd
    4. sed -r 's/(.*)(.)(.)$/13/' /etc/passwd
    5. sed -r 's/^([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/124/' /etc/passwd
    6. sed -r 's/([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)$/134/' /etc/passwd
    7. sed -r 's/([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)$/123/' /etc/passwd
    8. sed -r 's/^(.)(.*)(.)$/321/' /etc/passwd
    9. sed -r 's/^(.)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/42315/' /etc/passwd
    10. sed -r 's/^([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)$/52341/' /etc/passwd
    11. sed -r 's/[0-9]//g' /etc/passwd
    12. sed -r 's/^ *//g' /etc/passwd
    13. sed -r 's/ /	/g' /etc/passwd
    14. sed -r 's/[A-Z]/(&)/g' /etc/passwd
    15. sed 'p;p' /etc/passwd
    16. sed '1~2d' /etc/passwd
    
    选做题17-18:文件内容
    [root@MiWiFi-R3-srv ~]# cat test 
    11111111111
    2222222222
    333333333
    4444444444
    55555555555
    6666666666
    777777777777
    888888888888
    99999999999999
    
    17.sed '2h;3,5H;7G' test 
    
    18. sed '2h;3,5H;2,5d;7G' test 
    
    19. sed -r 's/^([a-Z]+)([^a-Z]+)(.*)/1/' /etc/passwd
    20. sed -r 's/^([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/523416/' /etc/passwd
    21. 
    22. sed -r '{1h;1d;2G;2h;2d;3G;3h}'
    23. 
    echo "2012/12/11" |sed -r 's///:/g'
    echo "2012/12/11" |sed -r 's#/#:#g'
    echo "2012/12/11" |sed -r 's@/@:@g'
    sed作业

    四 awk

    awk -F: '$1 == "root"{print $1,NR,NF}' /etc/passwd
    awk -F: 'NR>20 || NR<3{print $1,$3,$NF}' /etc/passwd
    awk -F: 'NR>1 && NR<3{print $1,NR,NF}' /etc/passwd
    awk -F: 'NR>1 && NR<4{print $1,NR,NF}' /etc/passwd
    awk -F: '$1~/^r/{print $1,NR,NF}' /etc/passwd
    awk -F: '/^root/{print $1,NR,NF}' /etc/passwd

    username=root
    awk -v var=$username -F: '$1 == var{print $1,NR,NF}' /etc/passwd

    打印uid在30~40范围内的用户名。
    打印第5-10行的行号和用户名
    打印奇数行
    打印偶数行
    打印字段数大于5的行
    打印UID不等于GID的用户名
    打印没有指定shell的用户
    awk作业

    awk详细

    语法
    awk [options] 'commands' files
    option
    -F 定义字段分隔符,默认的分隔符是连续的空格或制表符
    使用option中的-F参数定义间隔符号
    用$1,$2,$3等的顺序表示files中每行以间隔符号分隔的各列不同域
    NF变量表示当前记录的字段数
    -v 定义变量并赋值 也可以借用次方式从shell变量中引入

    command
    读前处理 行处理 读后处理
    1.读前处理 BEGIN{awk_cmd1;awk_cmd2}
    2.行处理:定址 命令
    定址方法: 正则,变量,比较和关系运算
    正则需要用//包围起来
    ^ 行首
    $ 行尾
    . 除了换行符以外的任意单个字符
    * 前导字符的零个或多个
    .* 所有字符
    [] 字符组内的任一字符
    [^] 对字符组内的每个字符取反(不匹配字符组内的每个字符)
    ^[^] 非字符组内的字符开头的行
    [a-z] 小写字母
    [A-Z] 大写字母
    [a-Z] 小写和大写字母
    [0-9] 数字
    < 单词头 单词一般以空格或特殊字符做分隔,连续的字符串被当做单词
    > 单词尾

    扩展正则 加 -r 参数 或转义
    sed -n '/roo?/p' /etc/passwd
    sed -rn '/roo?/p' /etc/passwd
    ? 前导字符零个或一个
    + 前导字符一个或多个
    abc|def abc或def
    a(bc|de)f abcf 或 adef
    x{m} x出现m次
    x{m,} x出现m次至多次(至少m次)
    x{m,n} x出现m次至n次

    NR变量定址
    NR 表示AWK读入的行数
    FNR表示读入行所在文件中的行数
    # awk '{print NR,FNR,$1}' file1 file2
    1 1 aaaaa
    2 2 bbbbb
    3 3 ccccc
    4 1 dddddd
    5 2 eeeeee
    6 3 ffffff
    #
    逻辑运算 可直接引用域进行运算
    == >= <= != > < ~ !~
    # awk 'NR==1 {print}' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    #
    3.命令 {print $0}
    4.读后处理 END {awk_cmd1;awk_cmd2;}


    AWK变量
    NR 当前记录的个数(全部文件连接后的统计)
    FNR 当前记录的个数(仅为当前文件的统计,非全部)
    FS 字段分隔符 默认为连续空格或制表符,可以使用多个不同的符号做分隔符 -F[:/]
    OFS 输出字符的分隔符 默认是空格
    # awk -F: 'OFS="=====" {print $1,$2}' /etc/passwd
    root=====x
    NF 当前读入行的字段个数
    ORS 输出记录分隔符 默认是换行
    # awk -F: 'ORS="=====" {print $1,$2}' /etc/passwd
    root x=====bin x=====
    FILENAME 当前文件名

    引用shell变量的方法
    # a=root
    # awk -v var=$a -F: '$1 == var {print $0}' /etc/passwd
    或者 把整个命令拆开传递,让shell变量外露,
    # awk -F: '$1 == "'$a'" {print $0}' /etc/passwd
    # a=NF
    # awk -F: '{print $'$a'}' /etc/passwd

    操作符
    赋值
    = += -= /= *=
    逻辑与 逻辑或 逻辑非
    && || !
    匹配正则或不匹配,正则需要用 /正则/ 包围住
    ~ !~
    关系 比较字符串时要把字符串用双引号引起来
    < <= > >= != ==
    字段引用
    $ 字段引用需要加$,而变量引用直接用变量名取
    运算符
    + - * / % ++ --
    转义序列
    \ 自身
    $ 转义$
    制表符
     退格符
    回车符
    换行符
    c 取消换行


    练习
    打印uid在30~40范围内的用户名。
    打印第5-10行的行号和用户名
    打印奇数行
    打印偶数行
    打印字段数大于5的行
    打印UID不等于GID的用户名
    打印没有指定shell的用户
    打印1..1000以内的7的倍数和包含7的数

    流程控制
    分支结构

    if (条件) 动作
    若有多个动作,则要用大括号将动作体包含起来 if (条件) {动作1;动作2}
    # awk -F: '{if ($1 == "root") print $1}' /etc/passwd
    root
    #
    # awk -F: '{if ($1 == "root") {print $1;print $6}}' /etc/passwd
    root
    /root
    #

    if (条件1)
    动作1
    else
    动作2
    # awk -F: '{if ($1 == "root"){print $1}else print $6}' /etc/passwd
    # awk -F: '{if ($1 == "root") print $1;else print $6}' /etc/passwd
    上面两个命令是等价的,要么用分号隔开,表示第一个动作体的结束,要么将动作体用大括号定位范围

    if (条件 1)
    动作1
    else if(条件 2)
    动作2
    else if(条件 3)
    动作3
    else
    动作4
    # awk -F: '{if ($1 == "root") print $1;else if ($1 == "seker") print $6;else if ($1 == "zorro") print $7;else print NR}' /etc/passwd
    root
    2
    3
    ...
    33
    /home/seker
    /bin/bash
    36

    条件 ? 动作1 : 动作2
    expr?action1:action2
    # awk -F: 'var=($3 >= 500)?$1:"system_user" {print $1" "$3" "var}' /etc/passwd
    # awk -F: '{print ($3>500?$1:$2)}' /etc/passwd

    练习
    将系统用户按UID分组标记 0 admin; 1-499 sysuser; 500+ users
    awk -F: '{if($3==0) print $1" "$3" ""admin";else if($3>=1&&$3<500) print $1,$3,"sysuser";else print $1,$3,"user"}' /etc/passwd

    输出样式
    %s是字符类型,%d数值类型
    printf默认是不输出换行的所以要加
    10和7是偏移量
    默认是右对齐,所有加个- 就是左对齐,就是把不足的位数用空格填充
    注意:格式与输出列之间要有逗号
    # awk -F: '{printf "%-10s %-10d %s ",$1,$3,$7}' /etc/passwd


    读前处理和读后处理
    # awk -F: 'BEGIN{i=1} {i++} END {print i}' /etc/passwd
    47
    #
    # awk -F: 'BEGIN {print NR,NF}' /etc/passwd
    0 0
    #
    # awk -F: 'END {print NR,NF}' /etc/passwd
    46 7
    #
    练习
    找出普通用户的用户名并统计数量
    # awk -F: 'BEGIN{i=0} $3 >= 500 {print $1;i++} END {print i}' /etc/passwd
    计算UID相加的总和;计算GID相加的总和
    # awk -F: 'BEGIN{i=0}{sum+=$3;i++}END{print i;print sum}' /etc/passwd
    # awk -F: 'BEGIN{i=0}{sum+=$3;gsum+=$4;i++}END{print i;print sum;print gsum}' /etc/passwd
    计算VSZ和RSS各自的和 并以M单位显示
    # ps aux | awk 'BEGIN{i=0}NR>=2{sum+=$5;i++}END{print sum/1024"M"}'
    # ps aux | awk 'BEGIN{i=0}NR>=2{vsum+=$5;rsum+=$6;i++}END{print vsum/1024"M";print rsum/1024"M";print i}'
    循环语句
    while(条件) {
    动作
    条件运算
    }
    # awk -F: '{while($3<3) {print $3,$1;$3++}}' /etc/passwd
    0 root
    1 root
    2 root
    1 bin
    2 bin
    2 daemon
    #
    BEGIN块可以独立使用,不需要引入文件
    # awk 'BEGIN{i=1;while(i<100) {print i;i++}}'
    练习
    打印100以内的偶数
    # awk 'BEGIN{i=1;while(i<100) {if (i%2==0) print i;i++}}'

    x=1
    do {
    动作1
    x++
    } while (x<5)
    # awk 'BEGIN{i=5;do{print i;i++}while(i<10)}'
    # awk 'BEGIN{i=5;do{print i;i++}while(i<1)}'

    for(预置;条件;递增) {
    动作
    }
    # awk 'BEGIN {for (x=1;x<=4;x++) print x }'
    1
    2
    3
    4
    #
    # awk 'BEGIN{for (i=1;i<=4;i++) {for (j=1;j<=4;j++) print i,j}}'


    练习
    使用嵌套的for循环,打印100-999之间的数,个十百位分别用一个for来打印
    # awk 'BEGIN{OFS="";for (i=1;i<=9;i++) {for (j=0;j<=9;j++) {for (n=0;n<=9;n++) print i,j,n}}}'
    打印乘法口诀表
    # cat 99.sh
    #!/bin/bash
    awk 'BEGIN{
    for(i=1;i<10;i++)
    {
    for(j=1;j<=i;j++)
    printf "%d*%d=%d ",j,i,j*i
    print
    }

    }'
    #

    打印金字塔
    # cat jin.sh
    #!/bin/bash
    awk 'BEGIN{
    num=5
    for(i=1;i<=num;i++)
    {
    for (n=1;n<=num-i;n++)
    printf "%s"," "
    for (j=1;j<=2*i-1;j++)
    printf "%s","*"
    print
    }
    }'
    #

    逆序输出每个字段
    达到这样既可
    /bin/bash
    /root
    root
    0
    0
    x
    root


    # awk -F: '{for (x=NF;x>0;x--) print $x}' /etc/passwd

    继续解决上一个试做题的格式问题
    # awk -F: '/bash$/{for (x=NF;x>0;x--) printf "%-13s",$x;printf " "}' /etc/passwd

    跳转语句
    break 跳出循环
    # awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) break;print x }}'
    1
    2

    continue 在达到循环底部之前终止当前循环 从新开始下一次循环
    # awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) continue;print x }}'
    1
    2
    4

    next 读入下一行 同时返回脚本顶部 这样可以避免对当前行执行其他操作
    # awk -F: 'NR > 5 {next} {print $1} END {print NR}' /etc/passwd
    root
    bin
    daemon
    adm
    lp
    46
    #
    exit 使读取动作终止 并将控制移动到END,如果没有END则终止脚本
    # awk -F: 'NR > 5 {exit} {print $1} END {print NR}' /etc/passwd
    root
    bin
    daemon
    adm
    lp
    6
    #

    数组
    自定义数组
    # awk 'BEGIN {ary[1]="seker";ary[2]="zorro";print ary[1],ary[2]}'
    seker zorro
    #
    # awk 'BEGIN {ary[1]="seker";ary[2]="zorro";for(i in ary) print ary[i]}'
    seker
    zorro
    #
    删除一个元素 对元素给空值并不能清除这个元素 要想清除一个元素需要使用delete ary[idx]
    # awk 'BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";ary[2]="";for(i in ary) print ary[i]}'
    seker

    blues
    # awk 'BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";delete ary[2];for(i in ary) print ary[i]}'
    seker
    blues
    #

    循环产生数组和取出数组
    # awk 'BEGIN{n=5;for (i=1;i<=n;i++) ary[i]=i+100;for(m in ary) print m,ary[m]}'
    4 104
    5 105
    1 101
    2 102
    3 103
    #

    # awk -F: '{ary[NR]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
    1 root
    2 bin
    3 daemon
    4 adm
    5 lp
    6 sync
    7 shutdown
    8 halt
    9 mail
    # awk -F: '{ary[$3]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
    10 uucp
    11 operator
    12 games
    13 gopher
    14 ftp
    32 rpc
    37 rpm

    ARGV 命令行中参数数组
    # awk '{for (i in ARGV) {print i,ARGV[i]}}' /etc/passwd /etc/fstab
    0 awk
    1 /etc/passwd
    2 /etc/fstab
    #### i 为下标; ARGV[i] 下标为i的值
    练习
    统计每种shell被使用的次数


    函数

    算术函数 int
    [root@stu254 ~]# awk 'BEGIN {print int(3.9415)}'
    3
    [root@stu254 ~]#


    随机数函数 rand() srand()
    rand() 取值 0 > r < 1 之间 默认的种子是系统时间 精确到秒
    srand()取值 0 > r < 1 之间 可以指定种子来影响rand()取值数 默认是系统时间 精确到秒

    [root@stu254 ~]# awk 'BEGIN {srand(222);print int(rand()*100000000)}'
    90204196
    [root@stu254 ~]#

    字符串函数
    substr(s,x[,y])
    返回字符串s中从位置x起至y的子串,如果没有给出y,则从x开始到结束.
    [root@stu254 ~]# awk 'BEGIN {x="abcdefxyz";print substr(x,4,3)}'
    def
    [root@stu254 ~]#

    大写小写
    sprintf() 本身并不能打印,做格式转换,将数字转换成ASCII字符
    # awk 'BEGIN {for(i=97;i<=122;++i) print tolower(toupper(sprintf("%c",i)))}'

    字符串长度
    length() 如果没有给定字符串则使用$0
    [root@stu254 ~]# awk 'BEGIN {print length("abcdefxyz")}'
    9

    gsub(/abc/,"ABC",x) 全局字符串替换
    从x中用匹配的abc正则替换成ABC
    [root@stu254 ~]# awk 'BEGIN {x="xyzabcxyzabcxyz";gsub(/abc/,"ABC",x);print x}'
    xyzABCxyzABCxyz
    [root@stu254 ~]# sub 第一次的替换
    [root@stu254 ~]# awk 'BEGIN {x="xyzabcxyzabcxyz";sub(/abc/,"ABC",x);print x}'
    xyzABCxyzabcxyz
    [root@stu254 ~]#

    gensub(r, s, h [, t]) Search the target string t for matches of the reg-
    ular expression r. If h is a string beginning
    with g or G, then replace all matches of r with s.
    Otherwise, h is a number indicating which match of
    r to replace. If t is not supplied, $0 is used
    instead.
    gensub(正则,替换,范围,目标串)
    [root@tch254 ~]# awk 'BEGIN{print gensub("zorro","AAAA","2","seker zorro zorro seker")}'
    seker zorro AAAA seker
    [root@tch254 ~]# echo seker zorro zorro seker | sed 's/zorro/AAAA/2'
    seker zorro AAAA seker
    [root@tch254 ~]#
    [root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","g");print}'
    seker AAAA AAAA seker
    [root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","2");print}'
    seker zorro AAAA seker
    [root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","h");print}'
    seker AAAA zorro seker
    [root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","1");print}'
    seker AAAA zorro seker
    [root@tch254 ~]#


    系统函数
    getline

    交互输入
    [root@stu254 ~]# awk -F: 'BEGIN {printf "Enter Number: ";getline ;for(i=1;i<=$0;i++) print i}'
    Enter Number: 3
    1
    2
    3
    [root@stu254 ~]#

    将输入赋值给变量
    [root@stu254 ~]# awk -F: 'BEGIN {printf "Enter Number: ";getline NUM;for(i=1;i<=NUM;i++) print i}'
    Enter Number: 3
    1
    2
    3
    [root@stu254 ~]#

    从文件中读入
    [root@tch254 ~]# awk -F: 'BEGIN {getline < "/etc/passwd" ; print $3" "$1}'
    0 root
    [root@tch254 ~]#

    #awk -F: 'BEGIN {while (getline < "/etc/passwd" > 0) print $3" "$1}'

    getline < "/etc/passwd" 从文件中读入,每次读取一行,默认情况下读取的次数等于awk自身引入文件的行数
    也可以放到for中来控制读取的次数
    > 0 测试读取的返回值,成功返回1,失败返回-1,0文件末尾

    从命令输出中输入
    [root@stu254 ~]# awk 'BEGIN {"uname -a"|getline ;print $3}'
    2.6.18-53.el5
    [root@stu254 ~]#


    system(command)
    系统命令要用""引起来
    [root@stu254 ~]# rm -rf abc/
    [root@stu254 ~]# awk 'BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}'
    [root@stu254 ~]# awk 'BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}'
    mkdir: 无法创建目录 “abc”: 文件已存在
    ERR
    [root@stu254 ~]#
    [root@tch254 ~]# awk 'BEGIN {if(system("mkdir abc 2>/dev/null") != 0 ) print "ERR"}'
    ERR
    [root@tch254 ~]#

    awk脚本的介绍 -f 与 #!/bin/awk -f

    使用awk添加系统用户
    [root@mail ~]# cat useradd.awk
    #!/bin/awk -f

    {
    system("useradd "$1";echo "$2"|passwd --stdin "$1)
    }
    [root@mail ~]# cat username
    myname 1234
    [root@mail ~]#
    [root@mail ~]# ./useradd.awk ./username
    Changing password for user myname.
    passwd: all authentication tokens updated successfully.
    [root@mail ~]#

    1.使用:或/符号做分隔符,将字段逆序输出/etc/passwd文件的每行
    
    2.
    # awk -F: 'NR < 11 {print $3,$1}' /etc/passwd > name.txt
    # awk -F: 'NR < 11 {print $3,$6}' /etc/passwd > home.txt
    # join name.txt home.txt 
    观察两个文件,以及join命令输出,用awk引入name.txt,home.txt两个文件,模拟joni命令的输出
    
    3.统计/etc/passwd中每种shell的被使用人数
    输出格式:
    counts    shell
    1    
    1    /bin/sync
    4    /bin/bash
    31    /sbin/nologin
    1    /sbin/halt
    1    /sbin/shutdown
    
    4.统计ps中RSZ,VSS各自总和
    输出格式:
    ps MEM statistic
    VSZ_SUM : 164.277M
    RSS_SUM : 47.8555M
    
    
    5.计算/etc/passwd中所有用户的UID平均数,以及GID平均数.
    输出格式:
    UID and GID AVG
    UID-AVG : 1750.72
    GID_AVG : 1754
    
    6.
    根据uid值给用户分等级 Admin system users
    输出格式:
    
    LEVEL  NAME
    
    Admin    root
    sysuser    bin
    users    seker
    
    admin_count: N sys_user_count: N users_count: N 
    
    7.
    分别用GREP,SED,AWK将ifconfig中匹配到eth1的网卡所有信息打印出来.
    
    8.
    SHELL实现批量建立多个文件,将文件拓展名加上.txt,再加上.doc,再把中间的.txt去掉
    9.
    AWK脚本实现间隔五行打印表头
    [root@mail ~]# ./awk_print.sh /etc/passwd
    Username  Uid    
    root      0      
    bin       1      
    daemon    2      
    adm       3      
    lp        4      
    
    Username  Uid    
    shutdown  6      
    halt      7      
    mail      8      
    news      9      
    uucp      10     
    
    
    10.用$RANDOM产生100个随机数,交给AWK产生数组,在数组内部排序,最后输出.(禁止用sort命令)
    
    11. 建立 aa.txt 文本
    # cat aa.txt 
    aaa/bbb/ccc
    ddd/eee/fff
    ggg/hhh/iii
    aaa/bbb/ccc/aaa/bbb/ccc/ddd/eee
    # 用AWK或SED输出如下格式
    ccc aaa/bbb/
    fff ddd/eee/
    iii ggg/hhh/
    eee aaa/bbb/ccc/aaa/bbb/ccc/ddd/
    
    
    
    12. 建立 a.txt b.txt 文件
    [root@tch254 ~]# cat a.txt 
    1    a
    2    b
    3    c
    4    d
    5    e
    6    f
    7    g
    [root@tch254 ~]# cat b.txt 
    5    ABC
    3    DEF
    4    XYZ
    [root@tch254 ~]# 使用AWK处理两个文件输出如下结果
    e ABC
    c DEF
    d XYZ
    [root@tch254 ~]# 
    awk选做作业

    五 其他补充

    11111111111
    2222222222
    333333333
    4444444444
    55555555555
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    99999999999999
    99999999999999
    99999999999999
    99999999999999
    99999999999999
    99999999999999
    99999999999999
    aaaaaaaaaa
    6666666666
    777777777777
    888888888888
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    99999999999999
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    aaaaaaaaaa
    test文件内容

    [root@MiWiFi-R3-srv ~]# cat test |sort |uniq #排序去重
    11111111111
    2222222222
    333333333
    4444444444
    55555555555
    6666666666
    777777777777
    888888888888
    99999999999999
    aaaaaaaaaa


    [root@MiWiFi-R3-srv ~]# cat /etc/passwd |cut -d: -f2

    [root@MiWiFi-R3-srv ~]# find / -size +2M -type f -name *.txt

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

      

  • 相关阅读:
    软件部门每年耗资大约100亿到200多亿美元,但没有研发出任何具有说服力的产品,因此华为决定关闭
    Qt Model/View理解(二)---构造model(细心研读,发现超简单,Model就是做三件事:返回行数量、列数量、data如何显示。然后把model与view联系起来即可,两个例子都是如此)good
    linux 下用find命令查找文件,rm命令删除文件
    北电之死:谁谋杀了华为的对手?——银湖资本(Silver Lake)董事总经理爱德华·詹德出任CEO,既不了解华为,也不重视中国,直截了当地否决了收购华为
    搞定JavaScript正则表达式
    Orleans3.0
    CSS盒模型
    作用域和闭包
    NServiceBus是.Net平台下的开源的消息服务框架
    [1]System.Reflection.Emit
  • 原文地址:https://www.cnblogs.com/abdm-989/p/14094849.html
Copyright © 2011-2022 走看看