• awk 实用案例介绍


     

    awk 简介

    • awk是 3 个姓氏的首字母,代表该语言的 3 个作者
    • awk的版本有很多,包括: 旧版 awk,新版 awk(nawk), GNUawk(gawk)等
    • awk程序有 awk命令、括在引号或写在文件中的指令以及输入文件这几个部分组成

    从文件输入

    • 格式:
    –gawk '/匹配字符串/' 文件名
    –gawk '{处理动作}' 文件名
    –gawk '/匹配字符串/ {处理动作}' 文件名
    实例:
    [root@desktop243 Desktop]# gawk -F: '/student/{print $3}' /etc/passwd
    500
    #-F:以":"号为分隔符分割出参数
    [root@desktop243 Desktop]# gawk -F: '/student/{print $1,$3}' /etc/passwd
    student 500
    [root@desktop243 Desktop]# gawk -F: '/student/{print $0}' /etc/passwd
    student:x:500:500::/home/student:/bin/bash

    awk 工作原理

    以下面的内容的 names 文件名举例按步骤解析 awk 的处理过程
    [root@desktop243 Desktop]# vim names
    Tome Savage 100
    Molly Lee 200
    John Doe 300
    编辑这个 names文件时,Tome 行是多个出来空格,Molly 行是一个出来空格,John 是空格键打出来的空格。使用下面 awk命令处理
    cut 无法识别空格键和<Tab>打出来的空格
    [root@desktop243 Desktop]# cat names | cut -d ' ' -f 1
    Tome
    Molly Lee
    John
    [root@desktop243 Desktop]# cat names | cut -d ' ' -f 2
    Savage
    Doe
    gawk能区分空格键和<Tab>打出来的空格,“,”是代表打印时$1 和$3 之间有空格
    [root@desktop243 Desktop]# gawk '{ print $1,$3}' names
    Tome 100
    Molly 200
    John 300
    第一步:awk对文件或管道的内容一次只处理一行,将获取到
    的这一行赋给内部变量 $0
    第二步:这一行的内容按 awk内部变量 FS 定义的分隔符,缺省为空格(包括 tab 制表符)分解成字段,每一段存储在从 $1 开始的变量中
    第三步:awk中 print 命令打印字段
    –{print $1,$3} #只取有用的第一段和第三段
    –在打印时$1 和$3 之间由空格间隔。
    ","逗号是一个映射到内部的输出字段分隔符(OFS),OFS 变量缺省为空格,逗号在输出时被空
    格替换
    Tome 100
    Molly 200
    John 300
    • 接下来,awk处理下一行数据,直到所有的行处理完
    实例:
    OFS,输出字段分隔符定义为两个制表符,“,”在输出时被制表符替换
    [root@desktop243 Desktop]# gawk '{ OFS="		";print $1,$3}' names
    Tome 100
    Molly 200
    John 300

    从命令输入

    • awk还可以处理通过管道接收到的 Linux 命令的结果,shell程序通常使用 awk 做深处理
    • 格式:
    –命令 | gawk '/匹配字符串/'
    –命令 | gawk '{处理动作}'
    –命令 | gawk '/匹配字符串/ {处理动作}'
    df | gawk '$4 > 200000' #剩余空间大于 200000 的磁盘
    [root@desktop243 Desktop]# df | gawk '$3 > 100'
    Filesystem 1K-blocks Used AvailableUse% Mounted on
    16126920 2940932 12366788 20% /
    tmpfs 477200 260 476940 1% /dev/shm
    /dev/sda1 99150 58800 35230 63% /boot
    516040 21940 467888 5% /home

    格式化输出 print 函数

    awk命令操作处理部分是放在 "{}"(括号)中
    print 函数将变量和字符夹杂着输出,如同 linux 中的 echo 命令
    [root@desktop243 Desktop]# date
    Sat Oct 15 16:15:18 CST 2011
    [root@desktop243 Desktop]# date | gawk '{print "Month: "$2"
    Year:",$6}'
    Month: Oct
    Year: 2011
    注意上面的例子,一种是直接在Month空格后连接$2,另一种是在Year和$6 之间使用了逗号,都由 OFS 决定

    OFMT 变量

    在 OFMT 中定义数字的格式
    模拟分区大小的转换,保留小数点后 2 位:
    [root@desktop243Desktop]#echo -e "/dev/sda1	1234325
    /dev/sda3	2209"
    > | gawk '{OFMT="%.2f";print $1":",$2/1024,"M"}'
    /dev/sda1: 1205.40 M
    /dev/sda3: 2.16 M
    #$2/1024 前后必须要有","作为分隔定义,如果没有其中一个就会不能实现打印小数点后 2
    默认为"%.6gd",只会打印 6 位
    [root@desktop243Desktop]#echo -e "/dev/sda1 1234325 /dev/sda3 2209"
    |gawk '{prrnt $1":",$2/1024,"M"}'
    /dev/sda1: 1205.4 M
    /dev/sda3: 2.15723 M

    printf 函数转义字符

    printf 与 C 语言中的 printf 雷同
    –转义字符
    [root@desktop243 Desktop]# cat names
    Tome Savage 100
    Molly Lee 200
    John Doe 300
    names 文件中第 2 列的第一个字符:-%c 字符
    [root@desktop243 Desktop]# gawk '{printf("The charcter is %c
    ",$2)}' names
    The charcter is S
    The charcter is L
    The charcter is D
    names 文件中第 2 列的字符串:–%s 字符串
    [root@desktop243 Desktop]# gawk '{printf("The string is %s
    ",$2)}' names
    The string is Savage
    The string is Lee
    The string is Doe
    打印一个人的名字和年龄:–%d 十进制整数
    规定好了是字符串或者整数,后面的变量就必须是与之匹配的类型,否则会出错。
    [root@desktop243 Desktop]# echo "yangwawa 10"|gawk 
    > '{printf("%s is %d years old.
    ",$1,$2)}'
    yangwawa is 10 years old.
    –%f 浮点数
    [root@desktop243 Desktop]# echo "yangwawa 10 155"|gawk
    > '{printf("%s is %d years old,his heigth si %.2fm.
    ",$1,$2,$3/100)}'
    yangwawa is 10 years old,his heigth si 1.55m.

    printf 函数修饰符

    打印时需要对齐,下面提供一些打印输出时所用到的修饰符
    -(横杠) 左对齐和右对齐(默认),加| |是为了突出长度和对齐效果:
    [root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to 
    > |%s| lab
    ",$1)}'
    Welcome to |Bluefox|lab
    [root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to
    > |%10s|lab
    ",$1)}'
    Welcome to | Bluefox|lab
    [root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to
    > |%-10s|lab
    ",$1)}'
    Welcome to |Bluefox |lab
    #(井号) 显示 8 进制时前面加 0,显示 16 进制时加 0x
    +(加号) 显示正负值时的正+负-号
    0(零) 用 0 对显示值填充空白处
    练习:将文件(之前很乱的)names整理并保存,要求每一行每一列都要对齐:
    [root@desktop243 Desktop]# gawk '{printf("%s		%s		%d
    ",$1,$2,$3)}' 
    >names > name ;rm -f names ;mv name names
    [root@desktop243 Desktop]# cat names
    Tome Savage 100
    Molly Lee 200
    John Doe 300

    文件中的 awk 命令

    • 将 awk写入一个文件中,更适合复杂的程序
    • 使用 -f 选项指定 awk的文件名
    • awk一次读取一条记录,测试文件中的每一条命令这样循环
    根据下面显示结果分析代码:
    执行结果:
    [root@desktop243 Desktop]# cat names | gawk -f abc.awk
    ________--------________
    Nice to meet you -->Molly
    ________--------________
    ________--------________
    John
    代码:
    [root@desktop243 Desktop]# cat abc.awk
    #!/bin/gawk
    /^[Mm]olly/{print "Nice to meet you -->"$1}
    {print "________--------________"}
    $3>200 {print $1}
    被测试的文件:
    [root@desktop243 Desktop]# cat names
    Tome Savage 100
    Molly Lee 200
    John Doe 300
    分析:
    程序开始后首先读到的$0 是 Tome Savage 100,开始的字符既不是 M 也不是m, 后 面 的 {print "Nice to meet you -->"$1}不 会 执 行 , 执 行 {print "________
    --------________"}后,判断 100 不大于 200,则 {print $1}不执行;接着$0 是 Molly
    Lee 200,首字符串匹配,执行{print "Nice to meet you -->"$1}的结果是
    Nice to meet you -->Molly,然后执行下一句,执行最后一句时发现 200 不大于 200,
    {print $1}不执行;最后传给$0的是John Doe 300,第一句由于不匹配而没有
    执行,执行完第二句代码,最后 300 大于 200,所以执行{print $1},结果是显示 John。
    小小地修改一下代码:
    #!/bin/gawk
    /^[Mm]olly/{print "Nice to meet you -->"$1;
    print "________--------________"}
    $3>200 {print $1}
    再测试,结果:
    [root@desktop243 Desktop]# gawk -f abc.awk names
    Nice to meet you -->Molly
    ________--------________
    John

    记录与字段

    记录分隔符:默认行输入和输出的分隔符都是回车 ,保存在 RS 和 ORS 内部变量中
    实例:
    默认:
    [root@desktop243 Desktop]# gawk '{print $1,$2}' names
    Tome Savage
    Molly Lee
    John Doe
    自定义:
    [root@desktop243 Desktop]# gawk 'ORS="
    +-+
    "{print $1,$2}' names
    Tome Savage
    +-+
    Molly Lee
    +-+
    John Doe
    +-+
    变量$0: awk每次一行取得整条记录,$0 随之改变,同时内部变量 NF(字段的总数,即列数)也随之变化
    实例:添加一个只有 2 列的数据,测试 NF:
    [root@desktop243 Desktop]# echo "Mayy Daly" >> names
    [root@desktop243 Desktop]# gawk '{print NF,$0}' names
    3 Tome Savage 100
    3 Molly Lee 200
    3 John Doe 300
    2 Mayy Daly
    变量 NR: 每条记录的行号,处理完一行将会加 1,所以全部处理完后可以理解成行数的总数
    [root@desktop243 Desktop]# gawk '{print NR,": ->",$0}' /etc/passwd
    1 : -> root:x:0:0:root:/root:/bin/bash
    2 : -> bin:x:1:1:bin:/bin:/sbin/nologin
    …… ……
    38 : -> student:x:500:500::/home/student:/bin/bash
    39 : -> visitor:x:501:501::/home/visitor:/bin/bash

    字段分隔符

    • FS 内部变量:
    – 保存着输入字段的分隔符的值 (OFS 则代表输出的分隔符)
    – 默认使用空格或制表符来分隔字段
    – 在 BEGIN 语句段中设置 FS 的值
    实例:
    [root@desktop243Desktop]# cat /etc/passwd | gawk 'BEGIN{FS=":";print"--count
    normal user now--"}$3 >= 500 { print $1;count++} END {printf("Total : %d
    Normal users
    ",count)}'
    --count normal user now--
    nfsnobody
    student
    visitor
    Total : 3 Normal users
    也可以在命令行中指定 -F 选项改变分隔符
    默认是以空格为分隔符
    [root@desktop243 Desktop]# gawk 'NR <= 4 {print NR,$1}' /etc/passwd
    1 root:x:0:0:root:/root:/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
    指定分隔符为“:”
    [root@desktop243 Desktop]# gawk -F : 'NR <= 4 {print NR,$1}' /etc/passwd
    1 root
    2 bin
    3 daemon
    4 adm
    • 使用多个字符分隔符,写在括号中,下面的例子使用空格,冒号和制表符
    – gawk -F'[ : ]' '{print $1, $5, $7 }' /etc/passwd

    模式

    awk模式用来控制输入的文本行执行什么样的操作
    • 模式为正则表达式
    • 模式具有着隐式 if 语句
    • 模式写在模式操作符两个 "//"中
    实例:
    [root@desktop2 Desktop]# gawk '/^root/' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    [root@desktop2 Desktop]# gawk '/^root/ || /^student/' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    student:x:500:500::/home/student:/bin/bash
    [root@desktop2 Desktop]# gawk -F: '/student/{print $1":"$3}' /etc/passwd
    student:500
    [root@desktop2 Desktop]# gawk '/^sshd/,/^stude*/' /etc/passwd
    sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
    nslcd:x:65:55:LDAP Client User:/:/sbin/nologin
    tcpdump:x:72:72::/:/sbin/nologin
    pulse:x:496:494:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
    gdm:x:42:42::/var/lib/gdm:/sbin/nologin
    student:x:500:500::/home/student:/bin/bash

    操作

    • 格式
    模式 { 操作语句 1; 操作语句 2; ...... ;}
    • 或者
    模式 { 操作语句 1 操作语句 2 ...... }

    正则表达式

    • 很多地方都是用到正则表达式来匹配特定的信息
    ^ 串首
    $ 串尾
    . 匹配单个任意字符
    * 匹配零个或多个前面的字
    + 匹配一个或多个前面的字符
    ? 匹配零个或一个前面的字符
    [ABC] 匹配括号中给出的任一个字符
    [A-Z] 匹配 A到 Z之间的任一个字符
    A|B 匹配二选一,或者的意思,等同于[AB]
    (AB)+ 匹配一个或多个括号中的组合
    * 星号本身,转义星号本身

    匹配操作符

    • 前面介绍了模式,也可以对特定的列使用模式匹配符"~",与条件做比较
    • 语法:
    • ~//
    或者
    !~ / /
    [root@desktop2 Desktop]# gawk -F: '$1 ~ /[sS]tud*/{print $1":"$3}' /etc/passwd
    student:500

    POSIX 字符类表达式

    [[:allnum:]] 字母和数字字符 [A-Za-z0-9]
    • [[:alpha:]] 字母字符等同于[A-Za-z]
    • [[:cntrl:]] 控制字符
    • [[:digit:]] 数字字符 [0-9]
    • [[:graph:]] 非空白字符(空格,控制字符等)
    • [[:lower:]] [[:upper:]] 小写/大写字母
    • [[:space:]] 所有的空白字符(换行符,空格,制表符)
    一般不会用这些,这些不好记。
    gawk 'BEGIN { FS=":"} $1 ~ /^[[:digit:]]+$/ {print $0 }' /etc/inittab
     

    awk 脚本

    • 如果有多条 awk的模式或指令要处理,将它们都写在脚本中
    • #为注释符
    • 一行多条命令要用分号隔开
    • 操作跟在模式之后的话,必须一行书写完(即左大括号在同一行)
    [root@desktop2 Desktop]#vim info
    #!/bin/gawk
    # The first awk script
    /student/ { print "Studentid is" , $2}
    /vistor/ {print "vistor id is", $2}
    运行:
    [root@desktop2 Desktop]#gawk -F: -f info /etc/passwd

    比较表达式

    用来对文本做比较,只有条件为真,才执行指定的动作
    • < gawk '$3 < 500' /etc/passwd #系统帐户
    • <= gawk '$3 <= 499' /etc/passwd #同上
    • == gawk '$3 == 0' /etc/passwd #id 为 0,root 帐户
    • != gawk '$3 != 500' /etc/passwd #非 id=500 的帐户
    • >= gawk '$3 >= 500' /etc/passwd #普通帐户
    • > gawk '$3 > 499' /etc/passwd #同上
    • ~ gawk '$1 ~ /^root/' /etc/passwd #root 帐户
    • !~ gawk '$1 !~ /^root/' /etc/passwd #非 root 帐户

    条件表达式

    • 格式:条件表达式 1?表达式 2:表达式 3
    • 与之等同的代码段如下
    {
    if (条件表达式 1 成立)
    表达式 2
    else
    表达式 3
    }
    实例:
    [root@desktop2Desktop]#echo "111 333"| gawk '{max=$1>$2?$1:$2;print max}'
    333
    [root@desktop Desktop]# cat /etc/passwd | gawk -F: '{printf("%s is %s
    user
    ",$1,$3 >= 500?"Normal":"System") }'
    rootis System user
    bin is System user
    …… ……
    student is Normal user
    visitor is Normal user

    算术运算

    可以在模式中执行计算操作
    awk '$3 / 1024 > 2000' filename
    + 加 10+2 =12
    - 减 10-2 =8
    * 乘 10*1024 =10240
    / 除 10240/1024 =10
    % 求模(求余数) 10%3=1
    ^ 次方 2^3=8

    逻辑操作符

    逻辑操作符用来测试模式的真假
    && 逻辑与 1&&0 FALSE
    || 逻辑或 1 || 0 TRUE
    ! 逻辑非 !0 TRUE
    awk -F: '$3 >500 && $3 <= 550' /etc/passwd
    awk -F: '$3 ==100 || $3 > 50'

    范围模式

    范围模式提供了选择一段数据操作的可能
    • 先匹配第一个模式,将作为开始部分
    • 接着匹配第二个模式,作为结束
    • 之间的这一段将选中做操作
    • 模式与模式之间使用","逗号间隔
    awk '/operator/,/nobody/' /etc/passwd

    验证数据的有效性

    验证数据的有效性可以综合我们之前所学的判断方式
    • 判断有效数据是否为七列,可以通过检测 NF 内部变量
    实例:找出 employees数据表中的缺空数据
    [root@shiyan ~]# cat employees
    Name Numbers Time Price
    Tome 24 2008/10/12 3000
    Marry 38 2008/11/8 5433
    Molly 70 2009/2/17 3421
    John 55 2009/4/9 5646
    Tome 65 2009/8/7
    Molly 45 2009/9/21
    John 77 2009/10/8 5687
    [root@shiyan ~]# gawk 'NF !=4 {print NR,": has only",NF,"fileds"}' employees
    6 : has only 3 fileds
    7 : has only 3 fileds
    • 判断是否为字母开头
    awk '$1 ~ /^[a-zA-z]+/ {.....}' filename
    • 判断数值是否大于某值
    awk '$4 > 200 {......}' filename

    数值变量和字符串变量

    字符串写下双引号中,比如"Hello world"
    • 默认将一个字符串转化成数字时,将变成 0
    –name = "Nancy" #name 此时为字符串
    –x++ #x 是数字,初始值为 1
    –number = 35 #number 是数字
    • 强行转化
    –name + 0 #name 此时变成数字 0
    –number " " #number 此时为字符

    用户自定义变量

    变量名可以是字母数字和下划线组成,但不能以数字开头。
    • awk将字符串变量初始化为空字符串 ""
    • 数值变量初始化为 0
    • 格式
    –变量 = 表达式
    [root@sya ~]#gawk'$1~/^T/{wage=$2*$4;print$1,$3,"wage:",wage}' ./employees
    Tome 2008/10/12 wage: 72000
    Tome 2009/8/7 wage: 0

    BEGIN 模式

    BEGIN 模式后面跟了一个程序段
    • 对输入文件进行任何处理之前,先执行 BEGIN 程序段
    • 用来修改内部变量(OFS,RS,FS)等的 值
    • 用来初始化用户变量和打印标题等
    [root@shiyan ~]# gawk 'BEGIN{FS=":";OFS="		";ORS="
    ****
    "} NR >=20 &&
    NR <= 23{print $1,$NF}' /etc/passwd
    smmsp /sbin/nologin
    ****
    nscd /sbin/nologin
    ****
    oprofile /sbin/nologin
    ****
    pcap /sbin/nologin
    ****
    [root@desktop1 Desktop]# echo "$(date +%F)" | gawk 'BEGIN{FS="-";} 
    > { print $1"/"$2"/"$3}'
    2011/10/18
    甚至无需文件名
    [root@shiyan ~]# gawk 'BEGIN{print "Hello World"}'
    Hello World

    END 模式

    不匹配任何输入行
    • 在 awk处理完输入行之后执行
    [root@shiyan ~]# gawk 'END{print "Total user:",NR}' /etc/passwd
    Total user: 36
    实例:查看虚拟主机有几台:
    [root@shiyan ~]#gawk '/^<Virt/ { total++ } END { print total, virtual hosts }'
    /etc/httpd/httpd.conf
    2 virtual hosts

    输出重定向

    可以将 awk 的输出重定向到 Linux 文件中
    • 目标文件必须用双引号括起来
    • "> " 清空文件,写入 awk信息,awk程序执行完毕后才关闭文件
    • ">>" 追加内容
    实例:将 employees 文件中的错误数据向屏幕输出,正确数据则将每个人每天的总金额输出到/root/cc 文件中:
    [root@shiyan ~]# vim rw.awk
    #!/bin/gawk
    { 
    if ( NF !=4 ) 
    { 
    print "Line",NR,":missing some info" 
    }else{ 
    print $1,"saled:",$2*$4 > "/root/cc"
    } 
    }
    测试:
    [root@shiyan ~]# gawk -f rw.awk employees
    Line 5 :missing some info
    Line 6 :missing some info
    [root@shiyan ~]# cat cc
    Tome saled: 72000
    Marry saled: 206454
    Molly saled: 239470
    John saled: 310530
    John saled: 437899

    输入重定向-读输入 getline

    从标准输入,管道或文件中读取输入
    • 读取每一行,重置 NF,NR 和 FNR 内部变量
    • 有内容读取到,返回 1 (为真值)
    • 读到 EOF(文件末尾) 返回 0 (为假值)
    • 发生错误,返回 -1
    [root@shiyan ~]# gawk 'BEGIN{"date"| getline DATE}{print DATE,$1,$7}'
    /etc/passwd
    20111015 日 星期六 09:41:49 CST root:x:0:0:root:/root:/bin/bash
    20111015 日 星期六 09:41:49 CST bin:x:1:1:bin:/bin:/sbin/nologin
    …… ……
    [root@shiyan ~]# gawk 'BEGIN{"date +%F"| getline DATE}{print DATE,$1,$7}'
    /etc/passwd
    2011-10-15 root:x:0:0:root:/root:/bin/bash
    2011-10-15 bin:x:1:1:bin:/bin:/sbin/nologin
    …… ……
    实例:提示用户输入一个用户名,判断该用户是在/etc/passwd 中的哪一行:
    [root@shiyan ~]# gawk 'BEGIN{print"What is your name?";
    >getline name < "/dev/tty"}
    >$1 ~ name{print "Found",name,"online",NR".";
    >print"NIce to meet you",name,"@_@"}' /etc/passwd
    Whatis your name?
    student #getline 是重定向到终端接受的
    Found student online 34.
    NIce to meet you student @_@

    Bash 向 awk 发送参数

    getline 函数也可以从 Bash 命令发送的管道中获取参数
    • 语法:
    –getline 变量 < "-" # "-" 如同 tar 一样代表着管道过来的信息
    [root@shiyan ~]# echo "$(date +%F)"|gawk 'BEGIN{ FS=":";
    >getline D < "-"}NR>=33{print D" "$1"/"$3":"$7}' /etc/passwd
    2011-10-15 avahi-autoipd/100:/sbin/nologin
    2011-10-15 student/500:/bin/bash
    2011-10-15 visitor/501:/bin/bash
    2011-10-15 oracle/502:/bin/bash

    管道

    打开管道的前提是其他管道必须关闭,每次只能打开一个管道,这和 Linux 中一条命令无
    限次的使用管道不同
    • 管道右边的命令必须写在双引号之间
    [root@shiyan ~]# gawk '/^[^#:]+/{print $1}' /etc/hosts
    127.0.0.1
    172.24.205.191
    172.24.200.254
    172.24.254.254
    [root@shiyan ~]# gawk '/^[^#:]+/{print $1 |"ping -c2 "$1}' /etc/hosts
    PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
    64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms
    connect: Network is unreachable
    connect: Network is unreachable
    connect: Network is unreachable
    64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
    --- 127.0.0.1 ping statistics ---
    2 packets transmitted, 2 received, 0% packetloss, time 999ms
    rtt min/avg/max/mdev = 0.028/0.032/0.037/0.007 ms

    关闭文件和管道

    要对 awk 程序中的某个文件或管道多次读写,得先关闭程序
    • awk程序在处理文件时会保持打开状态直到脚本结束。
    • END 块中也受管道影响。
    • 关闭文件或管道时的引号中的内容与打开文件或管道时的名称
    一致,甚至包括参数,空格
    • 使用 close()函数实现
    gawk '/^[^#]+/ { print $1 | "ping -c3 " $1 } END {close("ping -c 3 ")}' /etc/hosts

    system 函数

    • system()函数以 Linux 命令作为参数
    • 执行 Linux 命令之后将 Linux 命令 $? 退出值返回给 awk
    • 注意:函数中 Linux 命令必须用双引号
    [root@desktop1 Desktop]# gawk '/^[^#:]/{ if(system("ping -c2 "
    $1" > /dev/null")==0 ){print $1" is on line"}else{ print $1" is down"}}' /etc/hosts
    127.0.0.1 is on line
    172.24.0.2 is down
    172.24.200.254 is on line
    172.24.254.254 is on line
    
    注意里面的空格“ ” !

    if 语句

    • awk就如 C 语言,有着变量,条件判断和循环
    • if 语句是条件判断的关键字,默认为隐藏,
    • 格式
    if (判断的表达式)
    {
    语句 1;语句 2;…… #判断表达式(为真)成立而执行的代码段
    }
    实例:
    1、gawk -F: '{if( $3 >= 500) print $1 "is a normal user "}'/etc/passwd
    2、 gawk -F: '{ if ($3 >=500) {count++;} } END { print "Total normal user: "count }' /etc/passwd
     

    if/else 语句

    • if/else 语句实现了真与假的两重处理
    • 判断表达式为 1(真),与前面 if 语句相同
    • 为 0 (假)则执行 else 后面的代码段
    • 格式:
    if (判断表达式) {
    语句 1;语句 2;…… #判断表达式成立
    } else {
    语句 10,语句 11;…… #判断表达式不成立
    }
    实例 1:
    gawk -F: '{ if($3 >=500) { print $1 "is a normal user " } else { print $1 "is
    a system user "}' /etc/passwd
    实例 2:
    gawk -F: '{ if($3 < 500) { print $1 "is a system user " } else { print $1 "is a
    normal user "}' /etc/passwd
     

    if/else 和 else if 语句

    • 如果在计算多重判断的时候,我们还需要对 if/else 语句做扩充,在其后再加上 if else,做
    下一个判断
    语法
    if (判断表达式 1) {
    语句 1; 语句 2;…… #表达式 1 成立
    }else if (判断表达式 2) {
    语法 10;语法 11;...... #表达式 2 成立
    }else if (判断表达式 N...) {
    语法 N0;语法 N1;…… #表达式 N 成立
    }else {
    语法 Y1;语法 Y2;.... #以上都不成立
    }

    while 循环

    首先给初始一个变量,接着在 while 判断表达式中测试该变量,为 0(假)退出 while 循环;
    • 注意:代码段中要在一些情况下修改初始的变量值,否则是一个消耗 CPU 的死循环
    • 语法
    while(条件判断式){
    语句 1;语句 2;…… # 语句成立一直执行部分
    }
    [root@desktop Desktop]# gawk -F: '{print NR,":User info:
    ======";i=1;
    while(i<=NF){print $i;i++};print "
    "}' /etc/passwd
    1 :User info:
    ======
    root
    x
    0
    0
    root
    /root
    /bin/bash
    …… ……
    39 :User info:
    ======
    visitor
    x
    501
    501
    /home/visitor
    /bin/bash

    for 循环

    有着三个表达式,第一个为初始化变量,第二个做测试,第三个用来改变初始变量(如果缺少
    此部分,就得到代码段修改,否则是死循环)
    • 语法
    for(初始表达式; 判读表达式; 更新表达式) {
    语法 1; 语法 2; .....
    }
    [root@desktop Desktop]# gawk -F: '{print NR,":User info:
    ======";
    i=1;for(;i<=NF;i++){print $i};print "
    "}' /etc/passwd
    1 :User info:
    ======
    root
    x
    0
    0
    root
    /root
    /bin/bash
    2 :User info:
    ======
    bin
    x
    1
    1
    bin
    /bin
    /sbin/nologin
    …… ……

    循环控制

    break 用来终止循环
    • continue 语句 用来不做后续操作,绕过此次循环,继续下一循环
    实例:
    为了方便实验,创建一个/tmp/passwd:
    [root@localhost ~]# cat /tmp/passwd
    avahi-autoipd:x:100:103:avahi-autoipd:/var/lib/avahi-autoipd:/sbin/nologin
    student:x:500:500::/home/student:/bin/bash
    visitor:x:501:501::/home/visitor:/bin/bash
    harry:x:502:502::/home/harry:/bin/bash
    [root@desktop Desktop]# gawk -F: '{i=1;
    while(i++ < NF ){
    if($i~/daemon/)
    {print NR,$1;break;}
    }
    }' /etc/passwd
    3 daemon
    28 haldaemon
    31 avahi
    continue 语句:会将匹配的这行其他段打印出来,继续往下查询
    [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0;
    while(i++ < NF){if($i~/student/){ print "========";continue; }
    else { print $i ;}}}'
    avahi-autoipd
    x
    100
    103
    avahi-autoipd
    /var/lib/avahi-autoipd
    /sbin/nologin
    ========
    x
    500
    500
    ========
    /bin/bash
    visitor
    x
    501
    501
    /home/visitor
    /bin/bash
    harry
    x
    502
    502
    /home/harry
    /bin/bash
    break 语句:匹配的这行其他段都不会打印,继续往下查询
    [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0;
    while(i++ < NF){if($i~/student/){ print "========";break; }
    else { print $i ;}}}'
    avahi-autoipd
    x
    100
    103
    avahi-autoipd
    /var/lib/avahi-autoipd
    /sbin/nologin
    ========
    visitor
    x
    501
    501
    /home/visitor
    /bin/bash
    harry
    x
    502
    502
    /home/harry
    /bin/bash

    next 语句

    在循环处理时,使用 next 语句,将会使 awk 中跳过 next 段
    以后本次的处理,执行下一个循环
    • 下面的例子将打印出除系统帐户以外的所有用户
    [root@desktop18 Desktop]# gawk -F: '{if($3<500){next;}else{print $1}}'
    /etc/passwd
    nfsnobody
    student
    visitor

    exit 语句

    exit 用来终止 awk 对后续内容的处理
    • exit 也可以返回具体的值,提供 Bash 做状态判断
    • 要注意,exit 退出不会绕过 END 块,换句话说 END块
    总会执行,对于要向 BASH 返回值的处理,使用以下的方法
    • 语法:
    –{ exit(1) }
    实例:
    exit,找到匹配的就会退出,返回 1,不再往下查询:
    [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0;while(i++ <
    NF){if($i~/student/){ print "========";exit(1); } else { print $i ;}}}'
    avahi-autoipd
    x
    100
    103
    avahi-autoipd
    /var/lib/avahi-autoipd
    /sbin/nologin
    ========
    [root@localhost ~]# echo $?
    1

    关联数组的下标

    • awk中的数组下标可以是数字,也可以是字符串
    • 数组和变量一样,需要的时候直接创建
    • 数组的下标数值由 0 开始
    [root@desktop18 Desktop]# vim employess
    Tom 234 2008/03/05 685
    Mary 451 2008/04/06 932
    Bill 127 2009/10/09 3456
    Mary 341 2009/10/18 4532
    Tom 465 2009/11/10 3421
    [root@desktop18 Desktop]# gawk '{ name[k++]=$1} END{for(i=0;i<NR;i++){
    printi,name[i]}}' employess
    0 Tom
    1 Mary
    2 Bill
    3 Mary
    4 Tom

    用字符串作为数组的下标

    • 专门用于数组的 for 循环,遍历数组
    • for(索引 in 数组)
    {
    –语句
    }
    打印出用户名和总工资:数组下标:字符串
    将用户名($1)相同的行的工资($4)相加
    [root@desktop18 Desktop]# gawk '{ name[$1]+=$4} END{for(Name in name){
    print Name,":",name[Name]}}' employess
    Tom : 4106
    Mary : 5464
    Bill : 3456

    处理命令行参数

    从命令行获得参数,ARGC代表参数的总数,ARGV数组用来保存输入的参数
    [root@desktop18 Desktop]# vim argvs
    #!/bin/gawk
    BEGIN{
    for ( i=0;i < ARGC ; i++ )
    { 
    printf( "ARGV[%d] is --> %s
    ",i,ARGV[i])
    }
    printf( "Total : %d parameters
    ",ARGC);
    }
    [root@desktop18 Desktop]# gawk -f argvs /etc /root 1234 172.24.200.254
    ARGV[0] is --> gawk
    ARGV[1] is --> /etc
    ARGV[2] is --> /root
    ARGV[3] is --> 1234
    ARGV[4] is --> 172.24.200.254
    Total : 5 parameters
    awk不会把 -f 以及后面的脚本认定为参数
    [root@desktop18 Desktop]# gawk -f argvs /etc "ls /root"
    ARGV[0] is --> gawk
    ARGV[1] is --> /etc
    ARGV[2] is --> ls /root
    Total : 3 parameters
    用 xargs来接收保存命令传来的参数并传递:(以空隔来分别参数)
    [root@desktop18 Desktop]# echo "172.24.200.254" | xargs ping -c2
    PING 172.24.200.254 (172.24.200.254) 56(84) bytes of data.
    From 192.168.117.167 icmp_seq=1 Destination Host Unreachable
    From 192.168.117.167 icmp_seq=2 Destination Host Unreachable
    --- 172.24.200.254 ping statistics ---
    2 packets transmitted, 0 received, +2 errors, 100% packetloss, time 3000ms
    pipe 2
    [root@desktop18 Desktop]# echo "172.24.200.254 /etc/passwd /root" | xargs
    gawk -f argvs
    ARGV[0] is --> gawk
    ARGV[1] is --> 172.24.200.254
    ARGV[2] is --> /etc/passwd
    ARGV[3] is --> /root
    Total : 4 parameters

    字符串 sub 和 gsub 函数

    sub 和 gsub 函数,可以在条目中查找与给定的正则表达式匹配的字符串,并取代它
    • sub 和 gsub 的区别是,前者只对匹配部分一次替换,后者为全部替换
    • 语法:
    –sub( 正则表达式 , 替换字符串) #默认为$0,整条记录
    –sub( 正则表达式,替换字符串 , 目标字段); #指定的字段
    换名字:
    [root@desk18Desktop]# gawk '{sub(/[Tt]om/,"ttooomm",$1);print $0}' employess
    ttooomm 234 2008/03/05 685
    Mary 451 2008/04/06 932
    Bill 127 2009/10/09 3456
    Mary 341 2009/10/18 4532
    ttooomm 465 2009/11/10 3421
    sub:只对匹配部分一次替换
    [root@desktop18 Desktop]# gawk '{sub(/0/,"kkkkk");print $0}' employess
    Tom 234 2kkkkk08/03/05 685
    Mary 451 2kkkkk08/04/06 932
    Bill 127 2kkkkk09/10/09 3456
    Mary 341 2kkkkk09/10/18 4532
    Tom 465 2kkkkk09/11/10 3421
    gsub 全部替换
    [root@desktop18 Desktop]# gawk '{gsub(/0/,"kkkkk");print $0}' employess
    Tom 234 2kkkkkkkkkk8/kkkkk3/kkkkk5 685
    Mary 451 2kkkkkkkkkk8/kkkkk4/kkkkk6 932
    Bill 127 2kkkkkkkkkk9/1kkkkk/kkkkk9 3456
    Mary 341 2kkkkkkkkkk9/1kkkkk/18 4532
    Tom 465 2kkkkkkkkkk9/11/1kkkkk 3421

    sub 函数示例

    • 注意正则表达式的使用方法
    • gawk '{ sub(/172.168.0./, "192.168.0." ); print}'/etc/sysconfig/iptables
    • gawk '{ sub(/Mac/,"MacIntosh",$1) ; print }' filename
    • gawk '{ gsub(/[Tt]om/, "Thomas" , $1); print }' datafile

    字符串长度 length 函数

    • length 函数取回字符串的字符数量
    • 格式
    –length(字符串)
    –length #不带参数,返回记录中的字符个数
    带参数:
    [root@desktop18 Desktop]# echo "123qwe" | gawk 'BEGIN{getline RR <"-";print
    length(RR)}'
    6
    [root@desktop18 Desktop]# gawk '{print $1,length($1)}' employess
    Tom 3
    Mary 4
    Bill 4
    Mary 4
    Tom 3
    不带参数:
    [root@desktop18 Desktop]# gawk '{print $1,length}' employess
    Tom 22
    Mary 23
    Bill 24
    Mary 24
    Tom 24

    字符 substr 函数

    • substr函数返回从字符串指定位置开始的一个子字符串。
    • 如果指定了子字符串的长度,返回字符串的对应的部分
    • 语法:
    –substr(字符串,起始位置)
    –substr(字符串,起始位置,子字符串长度)
    [root@desktop18 Desktop]# gawk '{print substr($1,2,length)}' employess
    om
    ary
    ill
    
    
    ary
    
    
    om
    
    
    [root@desktop18 Desktop]# gawk '{print substr($3,1,4)}' employess
    
    
    2008
    
    
    2008
    
    
    2009
    
    
    2009
    
    
    2009
    
    
    将 employess 中的日期换成另一种格式
    
    
    [root@desktop18 Desktop]# gawk '{print $1,$2,
    
    
    substr($3,9,2)"/"substr($3,6,2)"/"substr($3,1,4),$4}' employess
    
    
    Tom 234 05/03/2008 685
    
    
    Mary 451 06/04/2008 932
    
    
    Bill 127 09/10/2009 3456
    
    
    Mary 341 18/10/2009 4532
    
    
    Tom 465 10/11/2009 3421
     

    字符 match 函数

    • match 函数根据正则表达式返回其在字符串中出现的位置,未出现,返回 0
    • match 函数中变量
    RSTART 子字符串出现的起始位置
    RLENGTH 子字符串的长度
    而这些变量之后可以提供给 substr 来提取子字符串
    实例:文件中的时间格式不同的情况下需要提取年份
    [root@desktop18 Desktop]# gawk '{print $3}' employess 2008-03-05
    2008-04-06
    09/11/2009
    2009-10-18
    2009-11-10
    [root@desktop18 Desktop]# gawk '{match($3,/[12][0-9][0-9][0-9]/);
    print $1,substr($3,RSTART,RLENGTH)}' employess
    Tom 2008
    Mary 2008
    Bill 2009
    Mary 2009
    Tom 2009

    字符 split 函数

    • split 函数用来将一个字符串拆分成一个数组
    • 语法:
    –split(字符串, 保存数据的数组,字段分隔符)
    #注意 Split 函数切割的数组下标从 1 开始,0 永远为空
    –split(字符串, 保存数据的数组 ) #使用 FS 默认值
    [root@desktop18 Desktop]# cat database
    2010-04-22 car 10 1000
    2010-05-10 car 7 700
    2010-05-13 dog 8 80
    2010-06-11 bike 1 100
    统计 5 月份的销售总额:
    [root@desktop18 Desktop]# gawk '{
    >split($1,DATE,"-");
    > if(DATE[2] == 5){
    > count+=$4
    > }
    >}
    >END{
    > print "May : "count
    >}' database
    May : 780

    整数 int 函数

    • int 函数去掉小数点后面的数字,仅仅留下整数部分
    • 它不会做四舍五入操作
    [root@desktop18 Desktop]# gawk 'END {print 31/3}' database
    10.3333
    [root@desktop18 Desktop]# gawk 'END {printint(31/3)}' database
    10

    生成随机数

    • rand 函数生成一个大于或等于 0,小于 1 的浮点数
    • 当多次执行上面的脚本,每次都生成相同的数字
    • srand 函数,以当前时刻为 rand()生成一个种子
    • srand(x)把 x 设置为种子,通常,程序应该在运行过程中不断的改变 x 的值
    当没有种子时,生成的随机数不同文件相同行的随机数是一样的,文件有多少行就生成多少个随机数
    [root@desktop18 Desktop]# gawk '{print NR,rand()}' database
    1 0.237788
    2 0.291066
    3 0.845814
    4 0.152208
    [root@desktop18 Desktop]# gawk '{print NR,rand()}' /etc/passwd
    1 0.237788
    2 0.291066
    3 0.845814
    4 0.152208
    …… ……
    38 0.377663
    39 0.899756
    当有种子时,生成的随机数不同文件或同一文件相同行的随机数是不一样的,文件有多少行
    就生成多少个随机数
    [root@desktop18 Desktop]# gawk 'BEGIN{srand()}{print NR,rand()}' database
    1 0.78965
    2 0.970641
    3 0.284081
    4 0.819256
    [root@desktop18 Desktop]# gawk 'BEGIN{srand()}{print NR,rand()}' database
    1 0.532605
    2 0.737259
    3 0.105589
    4 0.0879184

    技能掌握测试题:

    •1  分析下面数据中,打印出每个销售员 5 月销售的总额
    vi sales
    Tom 2010-04-09 car 6 6000
    Mary 2010-05-07 car 1 1000
    Tom 2010-05-20 bike 15 1500
    Mary 2010-05-22 car 2 2000
    Tom 2010-06-17 car 1 1000
     
    •2  以下的数据需要转成 SQL 语句,方便插入到数据库中,注意年月日的格式
    vi sales
    Tom 04/09/2010 car 6 6000
    Mary 05/07/2010 car 1 1000
    Tom 05/20/2010 bike 15 1500
    Mary 05/22/2010 car 2 2000
    SQL 格式
    insert into sales value('Tom',2010-04-09,'car',6,6000)
     
    题1:
    [root@desktop215 Desktop]# gawk '{
    > split($2,DATE,"-"); 先将日期的月份拆分成一个数组
    > if(DATE[2] == 5){
    > name[$1]+=$5}
    > }
    # 如果月份是 5 月,就将以姓名(字符串)为数组下标,将相同名字的销售额相
    > END{
    > for(Name in name){
    > print Name,": 05",name[Name]}
    > }' sales
    Tom : 05 1500
    Mary : 05 3000
    方法 2:
    [root@desktop215 Desktop]# gawk '
    $2 ~ /05/{name[$1]+=$5}
    END{
    for(Name in name){
    print Name,": 05",name[Name]}
    }' sales
    Tom : 05 1500
    Mary : 05 3000

    题二:

    [root@desktop215 Desktop]# gawk '{OFS="	"; print $1,
    substr($2,7,4)"-"substr($2,4,2)"-"substr($2,1,2),
    $3,$4,$5}' sales2 > /root/Desktop/sales3
    [root@desktop215 Desktop]# cat /root/Desktop/sales3
    Tom 2010-09-04 car 6 6000
    Mary 2010-07-05 car 1 1000
    Tom 2010-20-05 bike 15 1500
    Mary 2010-22-05 car 2 2000
    
    导入数据库:
    
    mysql> use bluefox;
    Database changed
    
    mysql> create table sale( name varchar(10), time date, spes varchar(10), num
    int(11), money int(11) );
    Query OK, 0 rows affected (0.11 sec)
    mysql> select * from sale;
    +------+------------+------+------+-------+
    | name | time | spes | num | money |
    +------+------------+------+------+-------+
    | Tom | 2010-04-09 | car | 6 | 6000 |
    | Mary | 2010-05-07 | car | 1 | 1000 |
    | Tom | 2010-05-20 | bike | 15 | 1500 |
    | Mary | 2010-05-22 | car | 2 | 2000 |
    +------+------------+------+------+-------+
    4 rows in set (0.00 sec)
  • 相关阅读:
    streamsets 集成 cratedb 测试
    streamsets k8s 部署试用
    streamsets rest api 转换 graphql
    StreamSets sdc rpc 测试
    StreamSets 相关文章
    StreamSets 多线程 Pipelines
    StreamSets SDC RPC Pipelines说明
    StreamSets 管理 SDC Edge上的pipeline
    StreamSets 部署 Pipelines 到 SDC Edge
    StreamSets 设计Edge pipeline
  • 原文地址:https://www.cnblogs.com/zoujiaojiao/p/10938735.html
Copyright © 2020-2023  润新知