awk是由Alfred Aho 、Peter Weinberger 和 Brian Kernighan这三个人创造的,awk由这个三个人的姓氏的首个字母组成。awk其实可以看成是一门独立的编程语言,他支持条件判断、数组、循环等功能。awk共有两个版本(gawk与nawk版本),在linux中我们最常使用的是gawk,同时awk、grep、sed被称为Linux中的三剑客
[root@CodeSheep ~]# ll /usr/bin/awk lrwxrwxrwx. 1 root root 4 Feb 15 2019 /usr/bin/awk -> gawk
关于"三剑客"的特长
- grep 更适合单纯的查找和匹配文本
- sed 更适合编辑匹配的文本
- awk 更适合格式化文本,对文本进行较复杂格式处理
awk基础
语法
awk [options] '{pattern + action}' {filenames}
其中的action 我们最常用的就是print以及prinf,对于action来说,每次经过一行,都会当前行执行一边action
比如
awk '{print "1",NR}' /etc/passwd
你会发现有多少行,他就输出了多少个1 每行一个
awk的工作流程
首先awk并不是列操作,而是行操作,同样的他也是一行一行的处理的,其中$0表示当前行,比如在正常情况下我们输出全文是用cat来查看的,那么用awk的操作是这样的,另外awk的接受标准输入和文件
cat /etc/passwd awk '{print $0}' /etc/passwd # 如上的内容是一样的
awk中的分隔符
为了处理好每一行中的每一个字段,awk引入了分隔符的概念,分隔符有三种表现的形式,
一种是不输入任何不指定任何,则默认为空格
一种是自定义的分隔符 使用 -F 指定 比如 -F:
另外一种是使用 内置变量指定 -v FS='#'
自定义分隔符
既然默认的分隔符是空格,那么当然还有自定义的分隔符咯。我们使用-F选项来指定我们的分隔符
# 使用#分隔符 [root@CodeSheep ~]# cat demo3.text 123#123#dsdshj#dlsj# [root@CodeSheep ~]# awk -F# '{print $1}' demo3.text 123 [root@CodeSheep ~]# cat demo3.text 123#123#dsdshj#dlsj# [root@CodeSheep ~]# awk -v FS='#' '{print $1}' demo3.text 123
其实在awk中不仅仅有输入的分割符,还有输出的分隔符,默认也为空格
[root@CodeSheep ~]# awk -F# '{print $1,$2}' demo3.text 123 123
OFS(output field separator)可以指定输出的分隔符,用法与FS相同
[root@CodeSheep ~]# awk -v FS='#' -v OFS='++++' '{print $1,$2}' demo3.text 123++++123
awk的内建变量
awk支持内建变量和自定义变量
内建变量
- $0 当前行
- $1~$n 第n个字段
- $NF 最后一个字段
- NF 当前行被分割字段总数
- NR 当前行行号
- FNR 各文件分别计数的行号
- FS 输入分隔符
- OFS 输出分隔符
- RS 输入换行符
- ORS 输出换行符
- FILENAME 当前文件名
- ARGC 命令行参数的个数
- ARGV 数组,保存的命令行所给定的各参数
# 给每一行添加行号,输出当前行号,以及当前行的内容 [root@CodeSheep ~]# df | awk '{print NR,$0}' 1 Filesystem 1K-blocks Used Available Use% Mounted on 2 /dev/vda1 41147472 6848400 32395580 18% / 3 devtmpfs 930656 0 930656 0% /dev 4 tmpfs 941116 0 941116 0% /dev/shm 5 tmpfs 941116 452 940664 1% /run 6 tmpfs 941116 0 941116 0% /sys/fs/cgroup 7 tmpfs 188224 0 188224 0% /run/user/0 # 多个文件使用NR时,会根据的文件顺序累加序号 # FNR则会分开标序 # ARGV,ARGC [root@CodeSheep ~]# awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGC}' demo1.txt demo2.text awk demo1.txt demo2.text 3 # 其中ARGV作为数组,第一个参数是awk
自定义变量名
自定义变量的两种形式,一种是在action外面 使用选项指定变量,比如 -v name="hzj"
另外一种则是在action前的pattern中定义,比如 {name="hzj"; action->print name}
[root@CodeSheep ~]# awk -v name="hzj" 'BEGIN{print name}' hzj [root@CodeSheep ~]# awk 'BEGIN{name="hzj";print name}' hzj [root@CodeSheep ~]# name=hzj [root@CodeSheep ~]# awk -v name=$name 'BEGIN{print name}' hzj
awk 参数
options
-v name="xx" -v外部定义变量
pattern+action
抛开Pattern 我们先来使用{action}
# 输出文本内容 [root@CodeSheep ~]# df Filesystem 1K-blocks Used Available Use% Mounted on /dev/vda1 41147472 6848500 32395480 18% / devtmpfs 930656 0 930656 0% /dev tmpfs 941116 0 941116 0% /dev/shm tmpfs 941116 452 940664 1% /run tmpfs 941116 0 941116 0% /sys/fs/cgroup tmpfs 188224 0 188224 0% /run/user/0 [root@CodeSheep ~]# df | awk '{print $1}' Filesystem /dev/vda1 devtmpfs tmpfs tmpfs tmpfs tmpfs
awk是逐行处理的,也就是说当awk处理一个文本的时候,他会一行一行的处理内容。其中awk默认以 换行符 为标记识别每一行。另外对于每一行的处理中 awk会按照用户指定的 分隔符 去分隔当前行,如果没有指定,则默认使用空格作为分隔符,当出现多个空格的时候,awk会自动将连续的空格理解成为一个分隔符。
其中 我们将被awk处理后的每一行都使用了特定的变量标记
$0表示当前处理的整行
$1表示第一个字段
$2表示第二个字段
$NF表示最后一个字段
NF表示当前行被分割后字段总数
因此 假设一行文本被空格分为8段,则$NF表示$8 NF=8 则$7=$(NF-1)
# 输出多行 [root@CodeSheep ~]# df | awk '{print $1 $2 $3}' Filesystem1K-blocksUsed /dev/vda1411474726848536 devtmpfs9306560 tmpfs9411160 tmpfs941116452 tmpfs9411160 tmpfs1882240 [root@CodeSheep ~]# df | awk '{print $1, $2 ,$3}' Filesystem 1K-blocks Used /dev/vda1 41147472 6848536 devtmpfs 930656 0 tmpfs 941116 0 tmpfs 941116 452 tmpfs 941116 0 tmpfs 188224 0 # 自己添加字段 [root@CodeSheep ~]# df | awk '{print $1 ,"this print test"}' Filesystem this print test /dev/vda1 this print test devtmpfs this print test tmpfs this print test tmpfs this print test tmpfs this print test tmpfs this print test [root@CodeSheep ~]# df | awk '{print "$1="$1 , "testfield:""this print test"}' $1=Filesystem testfield:this print test $1=/dev/vda1 testfield:this print test $1=devtmpfs testfield:this print test $1=tmpfs testfield:this print test $1=tmpfs testfield:this print test $1=tmpfs testfield:this print test $1=tmpfs testfield:this print test #看上面的案例我们可以发现,当$1被双引号包裹的时候,他就是一个字符串,不再是变量
awk中的Pattern
其实action的主要操作就是print输出,简单来用就是输出内容,更复杂的就是对输出的内容进行格式化的操作。而Pattern所存在的两种模式,愈加加强了awk的能力。
awk中的逻辑
为了更好的格式化,awk中也带有逻辑参数,其中包括开始BEGIN和结尾END,他们之间用{}分隔,比如
awk -v test="test" 'BEGIN{print "1",NR}{print test}END{print "input end" }' /etc/passwd
你会发现他输出了很多个test,首先begin进入,然后输出1和行号0 紧接着不断的进入行输出test 在最后一行输入时 执行input end
特殊模式下的BEGIN与END
- BEGIN 模式指定了处理文本之前需要执行的操作
- END 模式指定了处理完所有行之后所需要执行的操作
[root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"}' $1 $2 [root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2}' $1 $2 Filesystem 1K-blocks /dev/vda1 41147472 devtmpfs 930656 tmpfs 941116 tmpfs 941116 tmpfs 941116 tmpfs 188224 [root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2} END{print "end for file"}' $1 $2 Filesystem 1K-blocks /dev/vda1 41147472 devtmpfs 930656 tmpfs 941116 tmpfs 941116 tmpfs 941116 tmpfs 188224 end for file
在BEGIN的模式下,首先awk会执行BEGIN中的action 之后才做去执行其他的action,同理,在执行完所有的action之后,awk回去执行END模式下面的action。需要注意的是两个特殊的模式BEGIN以及END都需要大写
于是 awk的格式化能力表露无疑了,提取字段再加上BEGIN与END的两种特殊模式,一张完整的表不就出来了嘛
关系运算符
< 小于 x < y <= 小于等于 x <= y == 等于 x==y != 不等于 x!=y > = 大于等于 x>=y > 大于 x>y ~ 正则匹配 x ~ /正则/ !~ 正则不匹配 x !~ /正则/
awk中的正则模式
既然上面的关系运算符中出现了正则,那我们就来讲一讲正则模式
需要注意的是 当使用{x,y}这种次数匹配的正则表达式时,需要配合--posix选项或者--re-interval选项
# 匹配/etc/passwd 中以 tss开头的行 [root@CodeSheep ~]# cat /etc/passwd | awk '/tss/{print $0}' tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin # 匹配/etc/passwd 中以 /bin/bash结尾的行 [root@CodeSheep ~]# cat /etc/passwd | awk '/\/bin\/bash$/{print $0}' root:x:0:0:root:/root:/bin/bash # 匹配/etc/passwd 中以/bin/bash结尾的行 到以 tss开头的行 [root@CodeSheep ~]# cat /etc/passwd | awk '/\/bin\/bash$/,/^tss/{print $0}' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin nscd:x:28:28:NSCD Daemon:/:/sbin/nologin mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false dockerroot:x:997:994:Docker User:/var/lib/docker:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin # 匹配行号大于2 且小于6的行 [root@CodeSheep ~]# cat /etc/passwd | awk 'NR>2 && NR<=6{print $0}' daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync
awk中的语法逻辑
在awk中,同样可以使用if for while等在编程语法中出现的逻辑判断
if(){语句1;语句2;}
# 输出df第一行 [root@CodeSheep ~]# df | awk 'NR==1{print $0}' Filesystem 1K-blocks Used Available Use% Mounted on [root@CodeSheep ~]# df | awk '{if(NR==1){print $0}}' Filesystem 1K-blocks Used Available Use% Mounted on
if(){语句1;语句2;}else{语句3;语句4;}
if(){语句1;语句2;}else if(){语句3;语句4;}else{语句5;语句6;}
for(初始化;表达式;更新){语句}
for(变量 in 数组){语句}
while(表达式){语句}
do{语句}while(条件)
# 循环输出1-10 [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){print i}}' 1 2 ... [root@CodeSheep ~]# awk 'BEGIN{i=1;while(i<=10){print i;i++}}' 1 2 3 ... # 使用continue和break [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){continue};print i}}' 1 2 4 ... [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){break};print i}}' 1 2 # 使用exit退出当前脚本 [root@CodeSheep ~]# awk 'BEGIN{for(i=1;i<=10;i++){if(i==3){break};print i}}' 1 2 [root@CodeSheep ~]# df | awk 'BEGIN{print "filed"}{exit}{print $1}END{print "end file"}' filed end file # 并不是直接结束,而是跳到了end # 使用next 直接跳到下一行 df | awk 'BEGIN{print "输出第一行的下一行"}{if(NR==1){next};print $0}' [root@CodeSheep ~]# df | awk 'BEGIN{print "输出第一行的下一行"}{if(NR==1){next};print $0;{exit}}' 输出第一行的下一行 /dev/vda1 41147472 6851112 32392868 18% /
sed基础
sed [参数][定址commands][inputfile]
参数:
--version 查看版本
--help 查看帮助
-n 关闭默认输出,默认自动打印所有行
-e 多点编辑,允许多个脚本指令被执行
-r 支持正则
-i 修改原文件内容
-f 支持使用脚本文件
命令:
/p 打印变动行
s/ 替换
y/ 替换
/n 替换第几个匹配的内容
/g 匹配所有行
/d 删除当前行
i 从当前行前插入
a 从当前行后插入
c 替换匹配行
使用案例
输出
输出passwd文件的1~3行 sed -n '1,3p' /etc/passwd 输出passwd文件的root~daemon行 sed -n '/^root/,/daemon/p' 输出passwd文件中uid是0或1的行 sed -n '/x:[0:1]:/p' /etc/passwd
删除
删除第一行 sed '1d' /etc/passwd 删除第一行和第三行 sed -e '1d' -e '3d' /etc/passwd sed '1d;3d' /etc/passwd 保留第一行 删除其他行 sed '1!d'
替换
匹配第一个并修改 sed 's/root/ROOT' /etc/passwd 匹配第二个并修改 sed 's/root/ROOT' /etc/passwd 匹配所有并修改 sed 's/root/ROOT/g' /etc/passwd 匹配修改成功后,打印变动行 sed -n 's/root/ROOT/pg' /etc/passwd 修改后另存为其他文件 sed -n 's/root/ROOT/pg;w passwd.md' /etc/passwd sed -n 's/root/ROOT/pgw passwd.md' /etc/passwd 在文件的每行前面添加#注释 sed -n 's/^/#/p' /etc/passwd 删除文件的第1个字符 sed 's/^.//1' /etc/passwd 删除文件的第二个字符 sed 's/.//2' /etc/passwd 在每一行的上面添加hello sed 'i hello' /etc/passwd 在第一行的上面添加hello sed '1a hello' /etc/passwd 将a~z替换成A~Z sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' /etc/passwd 替换匹配行 sed -n '1c hello' /etc/passwd 输出1行 sed -n '1q' /etc/passwd 打印奇数行 sed -n 'p;n' /etc/passwd 打印偶数行 sed -n 'n;p' /etc/passwd 打印匹配字符串的下一行 sed -n '/^root/{n;p}' /etc/passwd
grep基础
grep (global regular Expression Print) 全局正则表达式输出
grep (选项) pattern flie -A <显示行数> 除了显示符合匹配行,也显示匹配行后的n行 -B <显示行数> 除了显示符合匹配行,也显示匹配行前的n行 -C <显示行数> A+B 显示前后n行 -c 统计匹配行数的总数,仅显示 -e 实现多个选项件的逻辑关系 or -E 扩展的正则表达式 -f FILE:从FILE获取PATTERN匹配 -i --ignore-case 忽略字符大小写的差别 -n 显示匹配的行号 -o 仅显示匹配到的字符串 -v 反向匹配,显示不被pattern匹配到的行 -w 匹配整个单词
需要注意的地方
- egrep = grep -E 除了 < \> \b 以外其他正则都可以去掉
- 如果有要匹配的内容中,有( ; )这种的话最好加"" 比如 grep ";" test.txt
正则表达式
匹配内容
.匹配全部字符,不匹配空行
[]匹配指定范围内的任意单个字符
[^] 取反
[:alnum:] [0-9a-zA-Z]
[:alpha:] [a-zA-Z]
[:upper:] 或 [A-Z]
[:lower:] 或 [a-z]
[:blank:] 匹配空白字符
[:punct:] 标点符号
匹配次数
*匹配任意次 0-n次 ,贪婪模式(尽可能多匹配) .*匹配任意次 1-n次 \?匹配 0-1次 \+至少匹配1次 char\{n\} 匹配char n次 char\{n,m\} 匹配char n-m次 char\{,n\} 匹配char 最多n次 char\{n,\} 匹配char 至少n次
位置确定
^ 行首
$ 行尾
^$ 空白行
划组匹配
在正则表达示中使用之前匹配到的内容,即划组匹配
# 分组形式 \(\) # 读取方式 \1 \2 \3
