shell-awk详细笔记

shell
    # var="hexiaoqiang"
    # ${var//PATTERN/SUBSTI}:查找var所表示的字符串中,所有被PATTERN所匹配到的字符串,并将其全部替换为SUBSTI所表示的字符串;
    ${var/#PATTERN/SUBSTI}:查找var所表示的字符串中,行首被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串;
    ${var/%PATTERN/SUBSTI}:查找var所表示的字符串中,行尾被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串;
        notice:PATTERN中使用glob风格和通配符;
    查找删除:
        ${var/PATTERN/SUBSTI}:以PATTERN为模式查找var字符串中第一次的匹配,并删除之;
        ${var//PATTERN}:以PATTERN为模式查找var字符串中的匹配,并全部删除;
        ${var/#PATTERN}:以PATTERN为模式查找var字符串行首的匹配,并全部删除;
        ${var/%PATTERN}:以PATTERN为模式查找var字符串中的匹配,并全部删除;
        ${var/%PATERN}:以PATTERN为模式查找var字符串末尾的匹配,并删除之;
    字符串大小写转换:
        ${var^^}:把var中的所有的小写字符转换为大写${var,,}:把var中的所有大写字符转换为小写;
    变量赋值:
        ${var:-VALUE}:如果var变量为空,或未设置,那么返回value;否则,则返回var变量的值;
        ${var:=VALUE}:如果var变量为空,或未设置,那么返回VALUE,并将VALUE赋值给var变量;否则,则返回var变量的值;
        ${var:+VALUE}:如果var变量不空,则返回VALUE;
        ${var:?ERRROR_INFO}:如果var为空,或未设置,那么返回ERROR_INFO为错误提示;否则,返回var值;
    练习:写一个脚本,完成如下功能
        (1)提示用户输入一个可执行命令的名称;
        (2)获取此命令所依赖的所有库文件列表;
        (3)复制命令至某目标目录(例如/mnt/sysroot,即把此目录当作根)下的对应的路径中
            bash,/bin/bash ==> /mnt/sysroot/bin/bash
            useradd,/usr/sbin/useradd ==> /mnt/sysroot/lib64/ld-linux-x8664.so.2
        
        进一步:每次复制完成一个命令之后,不要退出,而是提示用户继续输入要复制的其它命令,并重复完成如上所描述的功能;知道用户输入"quit"脚本
bash特性:
    引用命令的执行结果:$(COMMAND) 或者 `` 反引号
    示例: mkdir $(date +%H-%M-%s)
    stat:查看文件或者文件系统的状态
    示例: stat /etc/fstab
          File: ‘/etc/fstab’
          Size: 465           Blocks: 8          IO Block: 4096   regular file
            Device: fd00h/64768d    Inode: 33554498    Links: 1
            Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
            Access: 2018-11-28 18:16:42.497510412 +0800
            Modify: 2018-11-17 23:20:14.801999430 +0800
            Change: 2018-11-17 23:26:58.495992201 +0800
            Birth: -
    linux  文件包含2类属性:
            元数据: metadata
            数据:date
    正则匹配:
        [[:upper:]] 所有的大写字母匹配
        [[:lower:]] 所有的小写字母匹配
        [[:alpha:]] 所有字母匹配
        [[:digit:]] 所有数字匹配
        [[:alnum:]] 所有字母数字匹配
        [[:space:]] 所有空白字符匹配
        [[:punct:]] 所有标点符号匹配
    [^] 匹配直指定范围外的单个字符
        [^[:upper]] 匹配任意大写字母之外的单个字符
        [^0-9]
        [^[:alnum:]] 匹配非数字字母之外的单个字符
IO重定向及管理
    程序:指令+数据
        程序:IO
        程序的数据流有三种:
            输入的数据流: <----- 标准输入(stdin),  键盘
            输出的数据流: -----> 标准输出(stdout), 显示器
            错误输出流:  -----> 错误输出(stderr), 显示器
    fd: file descriptor 文件描述符
        标准输入 0
        标准输出 1
        错误输出 2
    输出重定向  >
        特性 覆盖输出
                >>
        特性 追加输出
    set -C 
        禁止覆盖输出重定向向至已存在的文件
        此时可使用强制覆盖输出 >|
    set +C
        关闭上述特性  这个效果仅对当前shell有效
    错误输出流重定向:2> , 2>>
    合并正常输出流和错误输出流 
        &> , &>>
        COMMAND > /PATH/TO/SOMEFILE 2>&1
        COMMAND >> /PATH/TO/SOMEFILE 2>&1
        ----这个地方不太理解 要反复练习理解
    输入重定向 <
    tr命令
        tr [option] ... SET1 [SET2]
            把输入的数据当中的字符,凡是在set1定义范围内出现的,通通对位转换为set2出现的字符
        用法1: tr set1 set2 < /path/from/somefile
        用法2:    tr -d set1 < /path/from/somefile
        以上2中操作都不修改源文件
    Here Document : <<
        cat << EOF
        cat > /path/to/somefile << EOF 
    这个知识点很重要 很多次都没有理解 
管道:链接程序,实现将前一个命令的输出直接定向后一个程序当做输入数据流
    COMMAND1 | COMMAND2 | COMMAND3 | ...
tee 命令:
    COMMAND | tee /PATH/TO/SOMEFILE 既可以输出查看的文件 又保存至其他位置
    
    #!/bin/bash
    #
    cat << EOF 
    disk) show disks info
    mem) show memory info
    cpu) show cpu info
    *)QUIT
    EOF
    read -p "Your choice: " option
    if [[ "$option" == "disk" ]];then
        fdisk -l /dev/[sh]d[a-z]
    elif [[ "$option" == "mem" ]];then
        free -m
    elif [[ "$option" == "cpu" ]];then
        lscpu
    else
        echo "Unkown option"
        exit 3
    fi
    
    #!/bin/bash
    #
    for username in user21 user22 user23;do
        useradd $username
    done
    
    #求100以内所有正整数之和
    #!/bin/bash
    declare -i sum=0
    for i in {1..100};do
        echo "\$sum is $sum , \$i is $i"
        sum=$[$sum+$1]
        done
        
        
        
awk命令:
    FS 默认的内置输入变量  默认为空白字符 如果在awk命令行重新定义 只需要-v 定义就可以了 
    
    示例:
        awk -v FS=':' '{print $1}' /etc/passwd   指的是以 : 为分隔符好进行打印
        awk -v FS=':' '{print "hello:",$1}' /etc/passwd
        
    上面的2条命令也可以通过-F:就可以是分隔符为:号进行打印的需求了
    awk -F: '{print "hello\ world:\ ",$1}' /etc/passwd
    OFS 内置的输出变量符 默认为空白字符 可以自定义
    FS OFS 区别主要是输入判断的变量符号 和输出表现的变量符号  比较容易理解
    
    示例
        awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
    
    RS: input record seperator   输入时的换行符
    ORS: output record seperator 输出时的换行符
    
    示例:
        awk -v RS=' ' -v ORS='#' '{print}' /etc/passwd
        awk -v RS=' ' -v ORS='#' '{print}' /var/log/messages
        解释:输入的时候以空格为换行符 然后输出的时候 有空格的时候就会转换为#号 这个地方不好理解
        
    NF: number of field 字段数量 记录每行以默认空格计算有多少字段
        示例:
            awk '{print NF}' /etc/log/messages
            awk '{print NF}' /etc/passwd
    $NF: NF会得到每行以空格计算有多好字段 然后拿到这个字段数 然后打印这个字段的值
         取字段变量的最后一个值
         示例:
            awk '{print $NF}' /var/log/messages
awk 内部引用变量不需要加$ 直接引用即可    
    NR: 记录文件的行数 后面跟多个文件的时候 继续增加行数 而是不区分文件进行计数
        示例:
            awk '{print NR}' /etc/passwd
            awk '{print NR}' /var/log/messages /etc/passwd
            awk -F: '(NR>=2&&NR<=10) {print $1}' /etc/passwd
    FNR: awk后面跟多个文件的时候 分别记录文件的行数        
        示例:
            awk '{print FNR}' /etc/fstab /etc/passwd
    FILENAME 显示文件名 一个文件要是有多少行 就会显示多少遍
        示例:
            awk '{print FILENAME}' /etc/fstab /etc/passwd  不建议常用 但是思想是可以遍历文件多少行 每行都会处理
    ARGC: 命令行参数的个数
        示例:
            awk '{print ARGC}' /etc/passwd /etc/issue /etc/fstab 记录命令行参数的个数 awk和自己的参数算只能算一个 文件有多少行就会显示多少遍
            awk 'BEGIN{print ARGC}' /etc/passwd /etc/fstab /etc/issue 在打印前面加BEGIN就会只显示一遍
    ARGV: 数组,保存的是命令行所给定的各参数
        示例:
            awk '{print ARGV[0]}' /etc/passwd /etc/fstab /etc/issue 只会打印awk这个下标的数组 不加BEGIN的时候文件有多少行就会显示多少遍
            awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGV[3]}' /etc/passwd  /etc/fstab /etc/issue /var/log/messages 在前面print添加BEGIN之后就只显示一遍 根据下标打印相对应的数组值
2.2自定义变量
    -v var=value
        变量名区分大小写
    在program中直接定义
    示例:
        awk -v test="hello world" '{print test}' /etc/fstab 
        awk -v test="hello world" 'BEGIN{print test}' /etc/fstab
        print 打印变量的时候直接引用变量名 /etc/fstab 这个地方值利用的是行数 文件有多少行就会打印多少遍hello world 前面加BEGIN的时候只会打印一遍了
        awk '{test="hello world";print test}'   可以在print中直接定义变量
        awk 'BEGIN{test="hello world";print test}' /etc/fstab
2.3 printf 命令
    格式化输出: printf FORMAT,item1,item2,...
        FORMAT必须给出
        printf不会自动换行,需要显示给出换行控制符,\n
        FORMAT 中需要分别为后面的每个item指定一个格式化符号
        
        格式符:
            %c: 显示字符的ASCII码
            %d %i: 显示十进制整数 decimal intege 单词可能不太准确
            %e %E: 科学计数法数值显示
            %f: 显示为浮点数
            %g %G: 以科学计数法或浮点形式显示数值
            %s: 显示字符串
            %u: 无符号整数
            %%: 显示%自身
    示例:
        awk -F: '{printf "%s",$1}' /etc/passwd    把$1套到%s进行以字符串进行显示 默认不加换行符就会显示在一行中
        awk -F: '{printf "%s\n",$1}' /etc/passwd  \n就会每一行只显示一个用户名
        awk -F: '{printf "Username: %s\n",$1}' /etc/passwd  还可以加前缀提示字符进行显示
        awk -F: '{printf "Username: %s,UID: %i\n",$1,$3}' /etc/passwd
        awk -F: '{printf "Username: %s,UID: %i\n",$1,$3}' /etc/passwd        第一个%s后面不能加\n不然每一行都会在2行进行显示了 在最后一个FORMAT定义的变量之后加\n符号 这个是显示 用户名和UID  %d %i 都是以数值的方式显示用户的UID
    修饰符:
        #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度;
            %3.1f 默认这样的使用 以f居多
            默认对齐方式为右对齐
            
            示例:
                awk -F: '{printf "Username: %15s,UID: %d\n",$1,$3}' /etc/passwd 
                
        -: 表示左对齐
            示例:
                awk -F: '{printf "Username: %-15s,UID: %d\n",$1,$3}' /etc/passwd
                
        +: 显示数值的符号 正数的时候前面会有+号
            
            示例:
                awk -F: '{printf "Username: %15s,UID: %+d\n",$1,$3}' /etc/passwd
4.操作符
    算数操作符
    
    x+y, x-y, x*y, x/y, x^y[x的y次方], x%y[取模] 
    -x
    +x:转换为数值 [把字符串转换为数值]
    字符串操作符:没有符号的操作符,表示字符串连接
    赋值操作符:
        =,+=,-=, *=, /=, %=, ^=
        ++, --
    比较操作符:
        >, >=, <, <=, !=, ==
    模式匹配符:
        ~:左侧的字符是否能匹配右侧的字符
        !~:左侧的字符不匹配右侧的字符
    逻辑操作符:
        && 与
        || 或
        ! 非
    函数调用:定义函数 后面跟上()就可以了  ()里面可以加上调用的参数
        
        function_name(argu1,argu2,argu2,...)
        
    条件表达式:
        
        selector:条件挑选器
        ?:表示为真执行 if-true-expression
            否则执行 if-false-expression
        
        示例:
            selector?if-true-expression:if-false-expression
            awk -F: '{$3>1000?usertype="Common User":usertype="System or Systemuser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
            解释:-F: 定义打印格式 $3>1000 判断第三个字段是否大于1000 然后定义条件表达式 格式上面有解释,以:为分隔符进行判断 第一个为真后面就会打印 第二个为假后面会打印 字段使用双引号进行包含 printf 开始匹配打印 "15%s:%-s\n" printf的打印格式 %s默认为右对齐 15表示15个空格 %-s表示左对齐 $1是取字段第一个值 usertype是调用定义的变量字符串进行打印 后面是需要的条件输入文件位置
5、PATTERN
    (1) empty:孔模式,匹配每一行
    (2) /regular expression/:仅处理能够被此处的模式匹配到的行
    (3) relational expression:关系表达式:结果有"真"有"假":结果为"真"才会被处理;
        真:结果为非0值,非空字符串
    (4) line ranges:行范围
        startline,endline: /pat1/,/pat2/
        注意:不支持直接给出数字的格式
        
        示例:
            awk '/^UUID/{print $1}' /etc/fstab 在这儿就能实现UUID匹配行的打印 打印第一个字段 默认的变量还是以空格为分割
            awk '!/^UUID/{print $1}' /etc/fstab  打印匹配模式以外的行 然后打印第一个字段
            awk -F: '$3>100{print $1,$3}' /etc/passwd $3>100是进行模式匹配 然后再后面打印 就可以实现类似grep的功能了
            awk -F: '$3>100{printf "%15s:%-s\n",$1,$3}' /etc/passwd
            awk -F: '$3<100{printf "%15s:%-s\n",$1,$3}' /etc/passwd
            awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd 查找/etc/passwd中shell为/bin/bash的用户
            awk -F: '$NF~/bash$/{print $1,$NF}' /etc//passwd $NF模式匹配 ~进行模式匹配 匹配的内容要用//括起来 /bash$/以bash结尾的用户
            awk -F: '$NF!~/bash$/{print $1,$NF}' /etc//passwd 不以/bash$/结尾的在$NF后面加一个!就可以
            awk -F: '/^r/,/^s/{print $1}' /etc/passwd /pat1/,/pat2/ 模式匹配 从r字母开头的行匹配到s开头的行结束
            awk -F: 'BEGIN{print "    username    uid     \n---------------"}{print $1,$3}' /etc/passwd BEGIN 和 END的使用方法
            awk '/^[[:space:]]*linux16/{print}' /etc/grub2.cfg 打印这个文件中以开头为空格的字符 然后后面匹配Linux16的行
    (5) BEGIN / END 模式
        BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
        END{}:仅在文本处理完成之后执行一次;
        
            示例:
              awk -F: '{print "    username    uid     \n---------------"}{print $1,$3}END{print "=====================\n       end"}' /etc/passwd END用在最后进行使用 打印结尾 表示
6. 常用的action
    (1) Expressions
    (2) Control statements: if while等:
    (3) Compound statements:组合语句
    (4) input statements
    (5) output statements
7. 控制语句
    if(condition) {statements}
    if(condition) {statements} else {statements}
    while(condition) {statements}
    do {statements} while{condition}
    for(expr1;expr2;expr3) {statements}
    break
    continue
    delete array[index]
    delete array
    exit
    {statements}
    
    7.1 if-else
        
        语法:if(condition) {statements} else {statements} 使用场景:对awk取得的整行或某个字段做条件判断
        示例:        
        awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
        awk -F: '{if($3>100) print $1,$3}' /etc/passwd
        awk -F: '{if($3>100) {printf "Common user: %s\n",$1} else {printf "root or Systemuser: %s\n",$1}}' /etc/passwd
        if-else的时候要把{prinf}都用大括号个括住了 不然会报语法错误
        awk '{if(NF>5) print $0}' /etc/fstab 对字段数大于5的进行print
        df -Th | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>4) print $1}'
    7.2 while 循环

        示例:
            awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
            awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) print $i,length($i);i++}}' /etc/grub2.cfg 嵌套if循环判断
    7.3 do-while循环
        语法:do statement while(condition)
            意义:至少执行一次循环体
    7.4 for循环
        语法: for(expr1;expr2;expr3) statement
            for(variable assignment;condition;iteration process) {for-body}
        awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
        
        特殊用法:
            能够遍历数组中的元素:
                语法:for(var in array) {for-body}
    7.5 switch语句
        语法: switch(expression) {case VALUE1 or /REGEXP1/: statement;case VALUE2 or /REGEXP2/: statement;...;default: statement}
    7.6 break和continue
        break [n] 退出n层循环
        continue
    7.7 next
        提前结束对本行的处理而直接进入下一行;
        示例:
            awk -F: '{if($3%2!=0) next; print $1,$3}' /etc//passwd
8.array
    关联数组:array[index-expression]
    index-expression    
        (1)可使用任意字符串:字符串要使用双引号;
        (2)如果某数组元素事先不存在,在引用时,awk会自动创建,并将其初始化为"空串";
        若要判断数组中是否存在某元素,要使用"index in array" 格式进行
        weekdays[mon]="Monday"
        若要遍历数组中的每个元素,要使用for循环;
            for(var in array) {for-body}
            示例:
                awk 'BEGIN{weekdays["mon"]="moday";weekdays["tue"]="Tuesday"; for(i in weekdays) {print weekdays[i]}}'
                注意: var会遍历array的每个索引
                使用场景:统计打印结果中某个字符串出现的次数
                netstat -ntlp | awk '/^tcp\>/{state[$NF]++}END { for(i in state) {print i.state[i]}}'
                awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log 统计ip访问的次数 俗称报告生成器
                awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab  统计系统文件类型出现的次数
                awk '{for(i=1;i<NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab  统计文件单词出现的次数
                    {for(i=1;i<NF;$i++){count[$i]++}} 使用for循环做定义判断  NF记录的是文件字段出现的次数  count[$i]++ 则是使用$i下标进行统计次数 每次+1
9.函数
    9.1内置函数
        函数处理;
            rand();返回0和1直接的随机数 awk在系统第一次打印时 是随机的而后的打印都是保持第一次的随机数
        字符串处理;
            length([s]): 返回指定字符串的长度
            sub(r,s,[t]): 以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容  这个只会替换第一次替换不会 全局进行替换
            gsub(r,s,[t]):表示全局进行替换
            
            示例:
                 awk -F: '{print sub(o,O,$1)}' /etc/passwd  使用print的时候会打印结果1表示不成功 0表示成功
            
            split(s,a,[r]) : 以r为字符切割字符s,并将切割后的结果保存至a所表示的数组中
                
                示例:
                netstat -tan | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count) {print i,count[i]}}'
                详解: {split($5,ip,":")} 以第5个字段进行切割保存至ip数组中,":"以分隔符进行切割
                      ip[1] 是以":"切割之后拿到左面第一个字段的值
                      count[ip[1]]++ 拿到左面第一个字段下表的记录 每次都+1 做数组循环
                      {split($5,ip,":");count[ip[1]]++}END最后只打印一次
                      {for(i in count) {print i,count[i]}} 做数组循环判断进行打印结果

 

posted @ 2019-01-05 15:42  超我  阅读(417)  评论(0编辑  收藏  举报