shell编程(八):文本处理三剑客之awk

文章目录

    • awk的工作模式
      • 语法格式
    • awk的内置变量
      • 示例
    • 格式化输出printf
      • 格式说明符
      • 修饰符
      • 示例
    • awk模式匹配的两种方法
      • 第一种模式匹配:RegExp正则表达式匹配
      • 第二种模式匹配:关系运算匹配
    • awk中表达式的用法
    • awk动作中的条件及循环语句
      • 条件语句
    • 循环语句
    • awk中的字符串函数
      • 常用字符串函数
      • 示例
    • awk选项总结
      • 传递参数
    • awk中的数组
      • shell中数组用法
      • awk中的数组
      • awk数组示例
    • awk中getline用法

awk的工作模式

语法格式

第一种形式

awk 'BEGIN{}pattern{commands}END{}' file_name
  • BEGIN{}表示文本开始之前,进行{}内的操作
  • END{}表示文本开始以后,进行{}内的操作
  • pattern 表示对那些行进行操作,不写则默认是所有行
  • {commands}对行进行的操作,可以写多个命令
    第二种形式
cat file_name | awk 'BEGIN{}pattern{commands}END{}'

awk的内置变量

n内置变量 含义
$0 整行内容
1 − 1- 1n 当前行的第1-n个字段
NF 当前行的字段个数,即多少列
NR 当前行的行号,从1开始计数
FNR 多个文件处理时,每个文件行号单独计数,都是从0开始
FS 输入字段分隔符。不指定默认以空格或tab分割
RS 输入行分割符,默认回车换行
OFS 输出字段分隔符,默认空格
ORS 输出行分隔符。默认回车
FILENAME 当前输入的文件名
ARGC 命令行参数个数
ARGV 命令行参数数组

示例

  • list内容
java:python:scala
hadoop:spark:flume
jake:mike:coco
awk 'BEGIN{FS=":"}{print $1}' list # BEGIN中指定分隔符为:
awk 'BEGIN{FS=":"}{print NR}' list # 显示行号
awk 'BEGIN{FS=":"}{print NF}' list # 显示每行的列数
awk 'BEGIN{FS=":"}{print NF}' list list #行号为连续显示
awk 'BEGIN{FS=":"}{print FNR}' list list1 # 每个文件是单独计算行号
awk 'BEGIN{FS=":"}{print $NF}' list # 输出最后一列

格式化输出printf

格式说明符

格式符 含义
%s 打印字符串
%d 打印十进制数

修饰符

修饰符 含义
- 左对齐
+ 右对齐
+ 显示8进制在前面加0,显示16进制在前面加0x

示例

以字符串格式化打印 /etc/passwd中的第七个字段,并且以":" 作为分隔符

awk 'BEGIN{FS=":"} {printf "%s:",$7}' /etc/passwd

以10进制打印/etc/passwd中第三个字段,以":"作为分隔

awk 'BEGIN{FS=":"} {printf "%10d\n",$3}' /etc/passwd # 右对齐10个字符

浮点数打印

awk 'BEGIN{FS=":"} {printf "%0.2f\n",$3}' /etc/passwd # 浮点数打印 保留两位小数

awk模式匹配的两种方法

第一种模式匹配:RegExp正则表达式匹配

示例
匹配/etc/passwd文件中含有root字符串的所有行

awk 'BEGIN{FS=":"}/root/{print $0}' /etc/passwd

匹配/etc/passwd文件中以yarn开头的所有行

awk 'BEGIN{FS=":"}/^yarn/{print $0}' /etc/passwd

第二种模式匹配:关系运算匹配

  • 支持 常用关系运算符 > < >= <= == !=
  • 还支持 ~:匹配正则表达式
  • !~不匹配正则表达式

示例
匹配/etc/passwd中第三个字段小于50的行

awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/passwd

匹配第三个字段包含3个或3个以上的数字信息的所有行

awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/{print $0}' /etc/passwd  # /[0-9]{3,}/ 固定写法

布尔运算符匹配
|| 或
&& 与
! 非

匹配/etc/passwd文件中包含hdfs或yarn的所有行信息

awk 'BEGIN{FS=":"}$1=="hdfs" || $1=="yarn" {print $0}' /etc/passwd

awk中表达式的用法

符号 含义
+
* 乘法
-
/ 除法
% 取模
^或** 乘方
++x 先加1
x++ 后加1

统计/etc/services中空白行的数量

awk '/^$/{sum++} END{print sum}' /etc/services # /^$/ 表示空白行

统计学生分数平均值
学生课程文件如下:

Allen 80 90 96 98
mike 93 98 92 91
zhang 78 76 87 92
awk 'BEGIN{printf "%-8s%-8s%-8s%-8s%-8s%-8s\n","name","math","english","chinese","history","avg"} {total=$2+$3+$4+$5;avg=total/4;printf "%-8s%-8d%-8d%-8d%-8d%-0.2f\n",$1,$2,$3,$4,$5,avg}' stu.txt

awk动作中的条件及循环语句

条件语句

if(条件表达式)
	动作1
else if
	动作2
else
	动作3

示例
只打印/etc/passwd/中第3个字段的数值在50-100范围内的行

awk 'BEGIN{FS=":"}{if($3<100 && $3>50) print($0)}' /etc/passwd

将命令写到文件中,并且执行 main.awk

BEGIN{FS=":"}{if($4<100 && $3>50) print($0)}

执行如下语句

awk -f main.awk /etc/passwd

循环语句

while循环
do while
for循环,语法格式和c语言一致。
示例
计算1+2+3+…100的和,使用while、do while、for循环三种方式实现

# for循环
BEGIN{
        for(i=0;i<=100;i++)
        {
                sum+=i
        }
        print sum
}
# while 
BEGIN{
        while(i<=100){
                sum+=i
                i++
        }
        print sum
}
#do while
BEGIN{
        i=0
        do{
           sum+=i
           i++
        }while(i<=100)
        print sum
}

awk中的字符串函数

常用字符串函数

函数名 解释 函数返回值
length(str) 字符串长度 整数长度值
index(str1,str2) 在str1中查找str2的位置 返回位置索引,从1计数
tolower(str) 转成小写 转化后的字符串
toupper(str) 转成大写 转化后的字符串
substr(str,m,n) 从str的m个字符开始,截取n位 截取后的子串
split(str,arr,fs) 按照fs切割字符串,结果保存arr 切割后子串的个数
match(str,RE) 在str中按照RE查找,返回位置 返回索引位置
sub(RE,RepStr,str) 在str中搜索符合RE的字符串,将其替换为RepStr,只替换一个 替换的个数
gsub(RE,RepStr,str) 在str中搜索符合RE的字符串,将其替换为RepStr,替换所有 替换的个数

示例

统计/etc/passwd中每行,每个字段的长度

BEGIN{
    FS=":"
}
{   i=0
    while(i<=NF)
    {
        if(i==NF){
            printf "%d",$i
        }
        else{
            printf "%d:",$i
        }
        i++
    }
    print ""
}

搜索字符串"I have s dream" 中出现"ea"子串的位置

# 使用index
awk 'BEGIN{str="I have s dream";location=index(str,"ea");print location}'

# 使用match
awk 'BEGIN{str="I have s dream";location=match(str,"ea");print location}' 

字符串大小写转换

awk 'BEGIN{str="I have s dream";print tolower(str)}' 
awk 'BEGIN{str="I have s dream";print toupper(str)}'

字符串分割

awk 'BEGIN{str="I have s dream";split(str,arr," ");for(i in arr) print arr[i]}'

截取子串

awk 'BEGIN{str="I have s dream"; print substr(str,4,5)}'
awk 'BEGIN{str="I have s dream"; print substr(str,4)}' # 第四位开始 

字符串替换

# 将123替换为$ 符号。sub返回的个数,直接修改str
awk 'BEGIN{str="my 123 dream"; sub(/[0-9]+/,"$",str);print str}'

awk选项总结

选项 解释
-v 参数传递
-f 指定脚本文件
-F 指定分隔符
-V 查看awk版本号

传递参数

num1=20
var="hello world"
awk -v num2="$num1" -v var1="$var" 'BEGIN{print num2,var1}'

awk中的数组

shell中数组用法

定义

array=("Allen" "Mike" "Jick") # 空格分割

数组操作

# 打印元素
echo ${array[2]}
# 打印数组元素个数
echo ${#array[@]}
# 打印数组元素长度
echo ${#array[3]}
# 给元素复制
array[3]="Li"
# 删除元素
unset array[0] # 被删掉后 下标不变
# 分片访问
echo ${array[@]:1:3}
# 元素内容替换
${array[@]/e/E} # 只替换第一个e
${array[@]//e/E} # 替换所有的e

数组遍历

for a in ${array[@]}
do
    echo $a
done

awk中的数组

在awk中,使用数组时,不仅可以使用1,2,3…作为数组下标,还可以使用字符串作为下标
使用数字作为下标

str="Allen Jerry Mike"
split(str,array)
for(i=1;i<=length(array);i++)
	print array[i]

使用字符串作为下标

array["var1"]="Jin"
array["var2"]="Hao"
array["var3"]="Fang"
for(a in array)
	print array[a]

awk数组示例

统计tcp链接状态个数

netstat -an | grep tcp | awk '{a[$6]++}END{for(i in a) print i,a[i]}'

计算横向数据总合,计算纵向数据总和

  • 数据
    allen 80 90 87 91
    mike 99 100 100 80
    kobe 99 98 99 100
  • 脚本
BEGIN{
    printf "%-10s%-10s%-10s%-10s%-10s\n","name","yuwen","math","english","total"
}
{
    total=$2+$3+$4
    yuwen_sum+=$2 # 列之间的数累加
    math_sum+=$3
    enlish_sum+=$4
    printf "%-10s%-10s%-10s%-10s%-10s\n",$1,$2,$3,$4,total
}
END{
    printf "%-10s%-10s%-10s%-10s%-10s\n","",yuwen_sum,math_sum,enlish_sum,""
}

awk中getline用法

getline可以实现同时读取两个文件进行操作
示例

  • file1
A 1
B 2
C 3
  • file2
A
C
  • 脚本
BEGIN{
    while(getline<"file1"){a[$1]=$2}  # 将file1中加载到数组中
}
{
    if($1 in a) print $0  # 如果file2中的$1在数组a中 则打印
}
END{
}

你可能感兴趣的:(shell编程)