关于linux:Shell文本处理三剑客之awk

27次阅读

共计 4962 个字符,预计需要花费 13 分钟才能阅读完成。

awk 是一个文本处理工具,通常用于解决数据并生成后果报告。其命名源于三位创始人姓氏首字母:Alfred Aho、Peter Weinberger、Brian Kernighan。

语法:

  • awk [options] 'BEGIN{} pattern {commands} END{}' file
  • stdout | awk [options] 'BEGIN{} pattern {commands} END{}'

阐明:

  • options 选项
  • BEGIN{} 正式解决数据之前执行
  • pattern 匹配模式
  • {commands;...} 解决命令,可能多行
  • END{} 解决完所有匹配数据后执行

<!– more –>

内置变量

变量名 阐明
$0 整行内容
$1-$n 以后行的第 1 – n 个字段(列)
NF Number Field,以后行字段个数(多少列)
NR Number Row,以后行的行号,从 1 开始计数
FNR File Number Row,多文件解决时,每个文件行号独自计数,都是从 0 开始
FS Field Separator,输出字段分隔符(默认空格或 tab 键)
RS Row Separator,输出行分隔符(默认回车换行)
OFS Output Field Separator,输入字段分隔符(默认空格)
ORS Output Row Separator,输入行分隔符(默认回车换行)
FILENAME 以后输出的文件名字
ARGC 命令行参数个数
ARGV 命令行参数数组

示例:

# 以 : 分隔,输入第 1 列
➜  awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

# 以 -- 分隔成行,以 : 分隔成列,输入第 1、2 列
➜  awk 'BEGIN{FS=":";RS="--"} {print $1,$2}' /etc/passwd

# 以 : 分隔列,输入最初一列,因为 NF 变量是总列数
➜  awk 'BEGIN{FS=":"} {print $NF}' /etc/passwd

格式化输入(printf)

格局符 阐明 修饰符 阐明
%s 字符串 - 左对齐
%d 十进制 + 右对齐
%f 浮点数 # 八进制后面加 0,十六进制后面加 0x
%x 十六进制
%o 八进制
%e 迷信计数法
%c 单个字符的 ASCII 码

示例:

# printf "%+20s %-20s\n",$1,$7
#   - 左对齐;+ 右对齐
#   20 列宽,有余则补空
#   s 打印字符串
#   .3f 打印保留 3 位数的浮点数
➜  awk 'BEGIN{FS=":";OFS="-"}{printf"%+20s %20.3f %-20s\n",$1,$3,$7}' /etc/passwd
                root                0.000 /bin/bash
                 bin                1.000 /sbin/nologin
              daemon                2.000 /sbin/nologin
                 adm                3.000 /sbin/nologin
                  lp                4.000 /sbin/nologin

模式匹配(pattern)

  • RegExp/patern/
  • 关系运算<><=>===!=~ 正则匹配、!~ 非正则匹配、&& 与、|| 或、!

示例:

# 打印以 root 结尾的行
➜  awk 'BEGIN{FS=":"} /^root/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

# 打印第 3 列大于 1000 的行
➜  awk 'BEGIN{FS=":"} $3>1000 {print $0}' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin

# 打印第 7 列是 /sbin/nologin 的行
➜  awk 'BEGIN{FS=":"} $7=="/sbin/nologin"{print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 打印第 7 列以 nologin 结尾的行
➜  awk 'BEGIN{FS=":"} $7~/.*nologin$/ {print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 打印第 3 列大于 500,并且第 7 列以 nologin 结尾的行
➜  awk 'BEGIN{FS=":"} $3>500 && $7~/.*nologin$/ {print $0}' /etc/passwd
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
dockerroot:x:996:993:Docker User:/var/lib/docker:/sbin/nologin

计算表达式

示例:

# 数学计算
➜  awk 'BEGIN{x=10;y=2; print x+y}'
12
➜  awk 'BEGIN{x=10;y=2; print x*y}'
20
➜  awk 'BEGIN{x=10;y=2; print x^y}'
100
➜  awk 'BEGIN{x=10;y=2; print x**y}'
100
➜  awk 'BEGIN{x=10;y=x++; print x,y}'
11 10
➜  awk 'BEGIN{x=10;y=++x; print x,y}'
11 11


# 打印空行行号、统计空行数量
➜  awk 'BEGIN{idx=0;} /^$/ {idx++; print NR} END{print idx;}' /etc/passwd

流程管制语句

语法:

# 条件判断
if(condition1) {# do something} else if(condition2) {# do something} else {# do something}

# 循环
while(condition) {#do something}

do
    # do something
while(condition)

for(i=0;i<10;i++) {# do something}

示例:

# 如果第 3 列小于 10 并且第 7 列是 /sbin/nologin 的行打印 this is if
# 如果第 3 列大于 500 的打印 this is else if
# 否则打印 this is else
➜  awk 'BEGIN{FS=":"} {if($3<10 && $7="/sbin/nologin") {print"this is if"} else if($3>500) {print"this is else if"} else {print"this is else"}}' /etc/passwd
this is if
this is if
this is else
this is else if
this is else

# 计算 1-10 相加的后果
# 留神:变量不须要提前申明
➜  awk 'BEGIN{while(i<10) {sum+=i; i++}; print sum}'
45
➜  awk 'BEGIN{do { sum+=i; i++;} while(i<10); print sum}'
45
➜  awk 'BEGIN{for(i=0;i<10;i++) {sum+=i;}; print sum}'
45

字符串函数

函数名 阐明 返回值
length(str) 计算字符串长度 整数长度值
index(str,sub_str) 在 str 中查找 sub_str 的地位 地位索引,从 1 计数
tolower(str) 转小写 转换后的小写字符串
toupper(str) 转大些 转换后的大写字符串
substr(str,start,length) 从 str 第 start 个字符开始,截取 length 位 截取后到子串
split(str,arr,fs) 按 fs 拆分字符串,后果保留到 arr 拆分后子串的个数
match(str,reg) 在 str 中按 reg 查找,返回地位 索引地位
sub(reg,new_sub_str,str) 在 str 中搜寻合乎 reg 的子串,将其替换为 new_sub_str,只替换第一个 替换的个数
gsub(reg,new_sub_str,str) 相似 sub,替换所有 替换的个数

示例:

# sub(/oo/,"11",$1) 返回替换的个数;前面的 $1 为替换后的值
➜  awk 'BEGIN{FS=":"} {print length($1),toupper($1),substr($1,0,2),sub(/oo/,"11",$1),$1}' /etc/passwd
4 ROOT ro 1 r11t
3 BIN bi 0 bin
6 DAEMON da 0 daemon
4 SYNC sy 0 sync

# 数组下标从 1 开始
➜  awk 'BEGIN{str="Shell;Python;C;C++;Java;PHP"; split(str,arr,";"); print arr[2]}'
Python
➜  awk 'BEGIN{str="Shell;Python;C;C++;Java;PHP"; split(str,arr,";"); for(i in arr) {print arr[i]; }}'
C++
Java
PHP
Shell
Python
C

罕用选项(options)

  • -v 参数传递
  • -f 指定脚本文件
  • -v 指定分隔符
  • -V 查看 awk 版本

示例:

# 引入内部变量
➜  var1=10
➜  var2="hello awk"
➜  awk -v var1="$var1" -v var2="$var2" 'BEGIN{print var1,var2}'
10 hello awk

# 把所有操作抽离到一个独立文件
# 倡议:简单操作优先应用这种形式,更易于程序了解和治理
➜  touch script.awk
BEGIN{FS=":"}

{if($3<10 && $7="/sbin/nologin") {print "this is if"} else if($3>500) {print "this is else if"} else {print "this is else"}
}

➜  awk -f script.awk /etc/passwd

# -F: 相当于 BEGIN{FS=":"}
$ awk -F: '{print $1}' pwd
root
bin
daemon

数组

shell 中的数组操作如下:

操作 示例 输入
定义一个数组 arr=("Python" "PHP" "Java" "Go" "Rust")
某个数组元素(下标从 0 开始) echo ${arr[2]} Java
数组元素个数 echo ${#arr[@]} 5
某个元素的长度 echo ${#arr[0]} 6
批改元素值 arr[2]="JAVA"
删除数组元素 unset arr[1]
打印所有数组元素) echo ${arr[@]} Python JAVA Go Rust
分片拜访 echo ${arr[@]:0:2} Python JAVA
数组元素替换(找到的第一个) echo ${arr[@]/A/a} Python JaVA Go Rust
数组元素替换(所有) echo ${arr[@]//A/a} Python JaVa Go Rust
数组遍历 for a in ${arr[*]}; do echo $a; done

而 awk 中数组的应用略有不同,它应用 关联数组 提供数组性能,即数组的索引能够是 数字 任意字符串

语法示例:

# 定义
# 语法:array_name[index]=value
➜  awk 'BEGIN{arr[0]=0; arr["second"]="2"; print arr[0],arr["second"];}'
0 2

# 数组元素参加计算
➜  awk 'BEGIN{arr[0]=0; arr["second"]="2"; print arr[0]+3,arr["second"];}'
3 2

# 删除数组元素
# 语法:delete array_name[index]
➜  awk 'BEGIN{arr[0]=0;arr["second"]="2"; delete arr["second"]; print arr["second"];}'

# 遍历数组
# 形式一:for ... in 是无序输入
➜  awk 'BEGIN{str="Python Rust PHP Go"; arrLen=split(str,arr," "); for(i in arr){print i,arr[i] }}'
4 Go
1 Python
2 Rust
3 PHP
# 形式二:for(i=1;i<=len;i++) {...} 有序输入
➜  awk 'BEGIN{str="Python Rust PHP Go"; arrLen=split(str,arr," "); for(i=1;i<=arrLen;i++){print i,arr[i] }}'
1 Rust
2 Go
3 Python
4 PHP

正文完
 0