04文件处理工具sed+awk
一、sed介绍
1. sed的工作流程
- 首先sed把当前正在处理的行保存在一个临时缓存区中(也称为模式空间),然后处理临时缓冲区中的行,完成后把该行发送到屏幕上。
- sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会修改原文件。
- Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
2. sed使用方法
sed常见的语法格式有两种,一种叫命令行模式,另一种叫脚本模式。
2.1 命令行格式
- 格式
sed [option] 'commands[地址定位]' filename
说明:引用shell script中的变量应使用双引号,而非通常使用的单引号
option:
-e 进行多项编辑,即对输入行应用多条sed命令时使用
-n 取消默认的输出
-f 指定sed脚本的文件名
-r 使用扩展正则表达式 + ? {} () |
-i inplace,原地编辑(修改源文件)
- 常用命令和选项
命令1:
p 打印行
d 删除行
i\ 在当前行之前插入文本。多行时除最后一行外,每行末尾需用"\"续行
a\ 在当前行后添加一行或多行。多行时除最后一行外,每行末尾需用“\”续行
c\ 用此符号后的新文本替换当前行中的文本。多行时除最后一行外,每行末尾需用"\"续行
1059 sed -n 'p' a.txt
1060 sed -n '1p' a.txt
1061 sed -n '2p' a.txt
1062 sed -n '1,5p' a.txt
1063 cat -n a.txt
1064 sed -n '5,10p' a.txt
1065 sed -n '$p' a.txt
1066 sed '5p' a.txt
1067 sed -n '5p' a.txt
1068 sed '1d' a.txt
1069 sed -n '1d' a.txt
1070 sed '1d' a.txt
1071 sed '1,5d' a.txt
1072 sed '$d' a.txt
1081 sed '$a99999' a.txt
1082 sed 'a99999' a.txt
1083 cat -n a.txt
1084 sed '5chello world' a.txt
1085 sed 'chello world' a.txt
1086 cat -n a.txt
1087 sed '1,5chello world' a.txt
[[email protected] shell05]# sed 'i\
aaaaa\
bbbbb\
88888' 1.txt
# sed '$a\
yyyyy\
8888' 1.txt
[[email protected] shell05]# sed '/^user01/c888888' 1.txt
[[email protected] shell05]# sed '18chello world' 1.txt
选项:
-e -r
[[email protected] shell06]# grep '^[^a-z]' 1.txt
[[email protected] shell06]# sed -n '/^[^a-z]/p' 1.txt
[[email protected] shell06]# grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' 1.txt
192.168.0.254
[[email protected] shell06]# sed -nr '/([0-9]{1,3}\.){3}[0-9]{1,3}/p' 1.txt
192.168.0.254
命令2:
r 从文件中读取输入行
w 将所选的行写入文件
1062 sed '5r /etc/hosts' 1.txt
1063 sed 'r /etc/hosts' 1.txt
1064 sed '$r /etc/hosts' 1.txt
1065 sed '5w ./a.txt' 1.txt
1067 cat a.txt
1068 sed '1,5w ./a.txt' 1.txt
! 对所选行以外的所有行应用命令,放到行数之后
# sed -n '1!p' 1.txt
1072 sed -n '4p' 1.txt
1073 sed -n '4!p' 1.txt
1074 cat -n 1.txt
1075 sed -n '1,17p' 1.txt
1076 sed -n '1,17!p' 1.txt
s 用一个字符串替换另一个
g 在行内进行全局替换
1079 sed -n 's/root/ROOT/p' 1.txt
1080 sed -n 's/root/ROOT/gp' 1.txt
1081 vim 1.txt
1082 cat -n 1.txt
1083 sed -n 's/^#//gp' 1.txt
[[email protected] shell06]# sed -n '[email protected]/sbin/[email protected]@gp' a.txt
[[email protected] shell06]# sed -n 's/\/sbin\/nologin/itcast/gp' a.txt
[[email protected] shell06]# sed -n '10s#/sbin/nologin#itcast#p' a.txt
uucp:x:10:14:uucp:/var/spool/uucp:itcast
[[email protected] shell06]# sed -n '1,5s/^/#/p' a.txt 注释掉文件的1-5行内容
#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
& 保存查找串以便在替换串中引用 \(xxxxx\)
= 打印行号
[[email protected] shell06]# sed -n '/root/p' a.txt
root:x:0:0:root:/root:/bin/bash
[[email protected] shell06]# sed -n 's/root/#&/p' a.txt
#root:x:0:0:root:/root:/bin/bash
[[email protected] shell06]#
[[email protected] shell06]# sed -n 's/^root/#&/p' a.txt
#root:x:0:0:root:/root:/bin/bash
[[email protected] shell06]# sed -n 's/\(root\)/#\1/p' a.txt
#root:x:0:0:root:/root:/bin/bash
[[email protected] shell06]# sed -n 's/\(^root\)/#\1/p' a.txt
#root:x:0:0:root:/root:/bin/bash
[[email protected] shell06]#
[[email protected] shell06]# sed -n 's/^root/&#/p' a.txt
root#:x:0:0:root:/root:/bin/bash
[[email protected] shell06]# sed -n 's/\(^root\)/\1#/p' a.txt
root#:x:0:0:root:/root:/bin/bash
[[email protected] shell05]# sed -n '1,5s/^/#&/p' 1.txt
#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
[[email protected] shell05]# sed -n '1,5s/\(^\)/#\1/p' 1.txt
#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
[[email protected] shell05]# sed -n '/root/p;/root/=' 1.txt
root:x:0:0:root:/root:/bin/bash
1
[[email protected] shell05]# sed -ne '/root/p' 1.txt -ne '/root/='
root:x:0:0:root:/root:/bin/bash
1
[[email protected] shell05]# sed -ne '/root/=' -ne '/root/p' 1.txt
1
root:x:0:0:root:/root:/bin/bash
在1.txt文件中的第5行的前面插入“hello world”;在1.txt文件的第8行下面插入“哈哈哈哈”
[[email protected] shell05]# sed -e '5ihello world' -e '8a哈哈哈哈哈' 1.txt -e '5=;8='
选项:-i 慎用!!!
[[email protected] shell06]# sed -i '1,5s/^/#&/' a.txt
注意:
-ni 不要一起使用
p命令 不要再使用-i时使用
- 定址
定址用于决定对哪些行进行编辑。地址的形式可以是数字、正则表达式、或二者的结合。如果没有指定地址,sed将处理输入文件的所有行。
x 指定x行号 sed -n '5p' 1.txt
x,y 指定x到y行号 sed -n '1,5p' 1.txt
/key/ 查询包含关键字的行 sed -n '/root/p' 1.txt
/key1/,/key2/ 匹配包含两个关键字之间的行 sed -n '/^adm/,/^mysql/p' 1.txt
/key/,x 从匹配关键字的行开始到文件第x行之间的行(包含关键字所在行)sed -n '/^lp/,7p'
x,/key/ 从第x行开始到与关键字的匹配行之间的行
x,y! 不包含x到y行
[[email protected] shell05]# sed -n '/bash$/!p' 1.txt
注意:sed使用的正则表达式是括在斜杠线"/"之间的模式。
//以下命令是找出以lp开头或者以mail开头的行
说明:
-e 多次编辑
-r 扩展正则
- 其他命令讲解
y命令
该命令与UNIX/Linux中的tr命令类似,字符按照一对一的方式从左到右进行转换。
正则表达式元字符对y命令不起作用。与s命令的分隔符一样,斜线可以被替换成其它的字符。
# sed '39,41y/stu/STU/' /etc/passwd
# sed '39,41y/stu:x/[email protected]%/' /etc/passwd
-e 选项 多项编辑
-e是编辑命令,用于sed执行多个编辑任务的情况下。在下一行开始编辑前,所有的编辑动作将应用到模式缓冲区中的行上。
-i 选项 直接修改原文件
# sed -i 's/root/ROOT/;s/stu/STU/' 11.txt
[[email protected] shell05]# sed -i '17{s/YUNWEI/yunwei/;s#/bin/bash#/sbin/nologin#}' 1.txt
& 符号 保留查找字符串
# sed -n 's/^root/#&/p' passwd 注释掉以root开头的行
# sed -n -r 's/^root|^stu/#&/p' /etc/passwd 注释掉以root开头或者以stu开头的行
# sed -n '1,5s/^[a-z].*/#&/p' passwd 注释掉1~5行中以任意小写字母开头的行
# sed -n '1,5s/^/#/p' /etc/passwd 注释1~5行
或者
sed -n '1,5s/^/#/p' passwd 以空开头的加上#
sed -n '1,5s/^#//p' passwd 以#开头的替换成空
vim ——>ctrl+v——>I(行头插入)——>ESC
1140 sed -n '/^root/p' 1.txt
1141 sed -n 's/^root/#&/p' 1.txt
1142 sed -n 's/\(^root\)/#\1/p' 1.txt
1143 sed -nr '/^root|^stu/p' 1.txt
1144 sed -nr 's/^root|^stu/#&/p' 1.txt
= 打印行号
# sed -n '/bash$/=' passwd 打印以bash结尾的行的行号
# sed -ne '/root/=' -ne '/root/p' passwd
# sed -n '/nologin$/=;/nologin$/p' 1.txt
# sed -ne '/nologin$/=' -ne '/nologin$/p' 1.txt
q 退出
# sed '5q' 1.txt
# sed '/mail/q' 1.txt
# sed -r '/^yunwei|^mail/q' 1.txt
[[email protected] shell05]# sed -n '/bash$/p;10q' 1.txt
ROOT:x:0:0:root:/root:/bin/bash
1173 sed '/^#/d;/^$/d' /etc/vsftpd/vsftpd.conf
1174 sed -r '/^#|^$/d' /etc/vsftpd/vsftpd.conf
1175 sed -e '/^#/d' -e '/^$/d' /etc/vsftpd/vsftpd.conf
引申扩展:
h 把模式空间里的内容复制到暂存缓冲区 <覆盖>
H 把模式空间里的内容追加到暂存缓冲区
g 取出暂存缓冲区的内容,将其复制到模式空间
G 取出暂存缓冲区的内容,将其复制到模式空间,追加在原有内容后面
x 交换暂存缓冲区与模式空间的内容
暂存和取用命令:h H g G
# sed '1H;$G' /etc/hosts
# sed '1h;$G' /etc/hosts
# sed '1h;$g' /etc/hosts
# sed '1{h;d};$G' /etc/hosts
# sed '1h;1d;$G'
# sed '1h;2,$g' /etc/hosts
# sed '1h;2,3H;$G' /etc/hosts
# sed '1!G;h;$!d' /etc/hosts 将文件内容从最后一行到第一行输出
暂存空间和模式空间互换:x
[[email protected] shell06]# sed '3h;4x;5G' 11.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
[[email protected] shell06]# cat -n 11.txt
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
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
2.2 脚本格式
- 用法
# sed -f scripts.sed file //使用脚本处理文件
建议使用 ./sed.sh file
脚本的第一行写上
#!/bin/sed -f
xxx
- 注意事项
1) 脚本文件是一个sed的命令行清单。
2) 在每行的末尾不能有任何空格、制表符(tab)或其它文本。
3) 如果在一行中有多个命令,应该用分号分隔。
4) 不需要且不可用引号保护命令
5) #号开头的行为注释
- 示例
# cat passwd
stu3:x:509:512::/home/user3:/bin/bash
stu4:x:510:513::/home/user4:/bin/bash
stu5:x:511:514::/home/user5:/bin/bash
# cat sed.sh
#!/bin/sed -f
2a\
******************
2,$s/stu/user/
$a\
we inster new line
s/^[a-z].*/#&/
[[email protected] shell06]# cat 1.sed
#!/bin/sed -f
3a**********************
$chelloworld
1,3s/^/#&/
[[email protected] shell06]# sed -f 1.sed -i 11.txt
[[email protected] shell06]# cat 11.txt
#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
helloworld
3. sed和正则的综合运用
1、正则表达式必须以”/“前后规范间隔
例如:sed '/root/d' file
例如:sed '/^root/d' file
2、如果匹配的是扩展正则表达式,需要使用-r选来扩展sed
注意:
在正则表达式中如果出现特殊字符(^$.*/[]),需要以前导 "\" 号做转义
eg:sed '/\$foo/p' file
3、逗号分隔符
例如:sed '5,7d' file 删除5到7行
例如:sed '/root/,/ftp/d' file
删除第一个匹配字符串"root"到第一个匹配字符串"ftp"的所有行本行不找 循环执行
4、组合方式
例如:sed '1,/foo/d' file 删除第一行到第一个匹配字符串"foo"的所有行
例如:sed '/foo/,+4d' file 删除从匹配字符串”foo“开始到其后四行为止的行
例如:sed '/foo/,~3d' file 删除从匹配字符串”foo“开始删除到3的倍数行(文件中)
例如:sed '1~5d' file 从第一行开始删每五行删除一行
例如:sed -nr '/foo|bar/p' file 显示配置字符串"foo"或"bar"的行
例如:sed -n '/foo/,/bar/p' file 显示匹配从foo到bar的行
例如:sed '1~2d' file 删除奇数行
例如:sed '0-2d' file 删除偶数行 sed '1~2!d' file
5、特殊情况
例如:sed '$d' file 删除最后一行
例如:sed '1d' file 删除第一行
6、其他:
sed 's/.//' a.txt 删除每一行中的第一个字符
sed 's/.//2' a.txt 删除每一行中的第二个字符
sed 's/.//N' a.txt 从文件中第N行开始,删除每行中第N个字符(N>2)
sed 's/.$//' a.txt 删除每一行中的最后一个字符
[[email protected] shell05]# cat 2.txt
1 a
2 b
3 c
4 d
5 e
6 f
7 u
8 k
9 o
[[email protected] shell05]# sed '/c/,~2d' 2.txt
1 a
2 b
5 e
6 f
7 u
8 k
9 o
4. 课堂练习
- 将任意数字替换成空或者制表符
- 去掉文件1-5行中的数字、冒号、斜杠
- 匹配root关键字替换成hello itcast,并保存到test.txt文件中
- 删除vsftpd.conf、smb.conf、main.cf配置文件里所有注释的行及空行(不要直接修改原文件)
- 使用sed命令截取自己的ip地址
- 使用sed命令一次性截取ip地址、广播地址、子网掩码
- 注释掉文件的2-3行和匹配到以root开头或者以ftp开头的行
1、将文件中任意数字替换成空或者制表符
# sed -n 's/[0-9]/\t/gp' a.txt
# sed -n 's/[0-9]//gp' a.txt
2、去掉文件1-5行中的数字、冒号、斜杠
# sed -n '1,5s/[:0-9/]//gp' a.txt
# sed -n '1,5{s/://g;s/[0-9]//g;s/[/]//gp}' a.txt
# sed -n '1,5s#[0-9/: ]##gp' 1.txt
3、匹配root关键字的行替换成hello itcast,并保存到test.txt文件中
# sed '/root/chello itcast' a.txt |tee bb.txt
4、删除vsftpd.conf、smb.conf、main.cf配置文件里所有注释的行及空行(不要直接修改原文件)
# sed '/^#/d;/^$/d' vsftpd.conf
# sed -e '/^#/d' -e '/^$/d' vsftpd.conf
# sed -r '/^#|^$/d' vsftpd.conf
# sed -r '/^#|^;|^\t$|^$/d' smb.conf
# sed -r '/^#|^\t$|^$|^\ /d' main.cf
5、使用sed命令截取自己的ip地址
# ifconfig eth0|sed -n '2p'|sed -n 's/.*addr://pg'|sed -n 's/Bcast.*//gp'
10.1.1.1
# ifconfig eth0|sed -n '2p'|sed 's/.*addr://g'|sed 's/ Bcast:.*//g'
6、使用sed命令一次性截取ip地址、广播地址、子网掩码
# ifconfig eth0|sed -n '2p'|sed -n 's#.*addr:\(.*\) Bcast:\(.*\) Mask:\(.*\)#\1\n\2\n\3#p'
10.1.1.1
10.1.1.255
255.255.255.0
7、注释掉文件的2-3行和匹配到以root开头或者以ftp开头的行
# sed -nr '2,3s/^/#&/p;s/^ROOT|^ftp/#&/p' 1.txt
#ROOT:x:0:0:root:/root:/bin/bash
#bin:x:1:1:bin:/bin:/sbin/nologin
#3daemon:x:2:2:daemon:/sbin:/sbin/nologin
# sed -ne '1,2s/^/#&/gp' a.txt -nre 's/^lp|^mail/#&/gp'
# sed -nr '1,2s/^/#&/gp;s/^lp|^mail/#&/gp' a.txt
补充:
&和\(\) 保留匹配的字符串
[[email protected] shell05]# sed -n 's/^root/#&/p' passwd
#root:x:0:0:root:/root:/bin/bash
[[email protected] shell05]# sed -n 's/^root/&#/p' passwd
root#:x:0:0:root:/root:/bin/bash
[[email protected] shell05]# sed -n 's/\(root\)/#\1/p' passwd
#root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/#root:/sbin/nologin
[[email protected] shell05]# sed -n 's/\(root\)/\1#/p' passwd
root#:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root#:/sbin/nologin
[[email protected] shell05]# sed -n 's/^\(root\)/\1#/p' passwd
root#:x:0:0:root:/root:/bin/bash
[[email protected] shell05]# sed -n '/^mail/,/^ftp/[email protected]^@#&@p' /etc/passwd
#mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
#uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
#operator:x:11:0:operator:/root:/sbin/nologin
#games:x:12:100:games:/usr/games:/sbin/nologin
#gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
#ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
[[email protected] shell05]# sed -n '9,[email protected]^@#&@gp' /etc/passwd
#mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
#uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
#operator:x:11:0:operator:/root:/sbin/nologin
#games:x:12:100:games:/usr/games:/sbin/nologin
#gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
#ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
练习
1、写一个初始化系统的脚本
1)自动修改主机名(如:ip是192.168.0.88,则主机名改为server88.itcast.cc)
a. 更改文件非交互式 sed
/etc/sysconfig/network
b.将本主机的IP截取出来赋值给一个变量ip;再然后将ip变量里以.分割的最后一位赋值给另一个变量ip1
2)自动配置可用的yum源
3)自动关闭防火墙和selinux
2、写一个搭建ftp服务的脚本,要求如下:
1)不支持本地用户登录
2) 匿名用户可以上传 新建 删除
3) 匿名用户限速500KBps
二、awk介绍
-
awk是一种编程语言,主要用于在linux/unix下对文本和数据进行处理,是linux/unix下的一个工具。数据可以来自标准输入、一个或多个文件,或其它命令的输出。
-
awk的处理文本和数据的方式:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
-
awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
-
gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。
-
下面介绍的awk是以GNU的gawk为例的,在linux系统中已把awk链接到gawk,所以下面全部以awk进行介绍。
1. awk使用方法
- 命令模式
awk [options] 'commands' file(s)
option 部分
-F 定义字段分割符号,默认的分隔符是空格
-v 定义变量并赋值
command 部分:
'范围说明或正则表达式或者{awk命令语句1;awk命令语句2;...}'
'BEGIN{} {} END{}'
行处理前 行处理 行处理后
1、范围说明部分可以是BEGIN、END、逻辑表达式或者为空
2、awk命令语句间用分号间隔
3、引用shell变量需用双引号引起
举例:
awk '/root/' /etc/passwd
awk 'NR==1' /etc/passwd
awk 'NR==1{print $1}' /etc/passwd
- 脚本模式
awk [options] -f scriptfile file(s)
特点:
1、awk脚本是awk命令的清单
2、命令需要用分号间隔
3、#号开头的是注释行
4、#!/bin/awk -f
2. awk工作原理
# awk -F: '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符(RS)结束
(2)然后,行被:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始。
(3)awk如何知道用空格来分隔字段的呢? 因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空格
(4)awk打印字段时,将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$3之间
有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格
(5)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔
成字段并进行处理。该过程将持续到所有行处理完毕
3. awk基本应用
- 记录与字段相关内部变量
$0 :表示当前所有记录
$1,$2,$3...$n:awk中用该顺序形式表示files中每行以间隔符号分割的各列的不同字段
注:
awk默认以空格符为间隔符号将每行分割为单独的字段,也可以使用awk内置变量FS定义间隔符号
# awk -F: '{print $1,$7}' a.txt
# awk 'BEGIN{FS=":"} {print $1,$7}' a.txt
NF 表示当前记录的字段数(列数)
$NF 最后一列
$(NF-1) 倒数第二列
FNR/NR 行号
FILENAME 文件名
"\t" 制表符
"\n" 换行符
"" 打印字符串
FS 定义间隔符
OFS 定义输出字段分隔符
RS 输入记录分割符,默认换行
ORS 输出记录分割符,默认换行
print 打印函数
awk -F: 'NR==2{print $1,$(NF-1)}'
格式化输出:
print函数 类似echo
# date |awk '{print "Month: "$2 "\nYear: "$NF}'
# awk -F: '{print "username is: " $1 "\t uid is: "$3}' /etc/passwd
printf函数 类似echo -n
# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
# awk -F: '{printf "|%15s| %10s| %15s|\n", $1,$2,$3}' /etc/passwd
# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
%s 字符类型
%d 数值类型
占15字符
- 表示左对齐,默认是右对齐
printf默认不会在行尾自动换行,加\n
- 定义变量
# awk -v NUM=3 -F: '{ print $NUM }' /etc/passwd
# awk -v NUM=3 -F: '{ print NUM }' /etc/passwd
注意:
awk中调用定义的变量不需要加$
- 定址
关键字:
BEGIN:表示在程序开始前执行
END :表示所有文件处理完后执行
示例:
1. 打印最后一列和倒数第二列(登录shell和家目录)
2. 打印/etc/passwd里的用户名、家目录及登录shell
u_name h_dir shell
***************************
***************************
awk 'BEGIN{} {} END{}'
awk 'BEGIN{FS=":";print "u_name\th_dir\tshell\n**************************************" } {print $1"\t"$(NF-1)"\t"$NF} END{print "*****************************************"}'
3. 计算1~10的和
awk -v sum=0 '{sum += $i}END{print sum}'
4. 打印1-10的奇数和
for ((i=1;i<=10;i+=2));do echo $i;done|awk -v sum=0 '{sum+=$0};END{print sum}'
5. awk打印1-5
awk 'BEGIN{for(i=1;i<=5;i++) print i}'
[[email protected] shell05]# awk 'BEGIN{for(i=1;i<=10;i++) print i}'
1
2
3
4
5
6
7
8
9
10
[[email protected] shell05]# awk 'BEGIN{for(i=1;i<=10;i+=2) print i}'
1
3
5
7
9
[[email protected] shell05]# awk 'BEGIN{for(i=1;i<=10;i+=2) print i}'|awk '{sum+=$i}END{print sum}'
25
[[email protected] shell05]# awk 'BEGIN{ for(i=1;i<=5;i++) print i }'|awk -v sum=0 '{sum += $i};END{print sum}'
15
[[email protected] shell05]# awk 'BEGIN{ for(i=1;i<=5;i++) print i }'|awk -v sum=0 '{sum += $a};END{print sum}'
15
[[email protected] shell05]# awk 'BEGIN{ for(i=1;i<=5;i++) print i }'|awk -v sum=0 '{sum += $b};END{print sum}'
15
4. awk和正则的综合运用
- 逻辑运算符
== (等于)
!= (不等于) !=
> (大于)
< (小于)
>= (大于等于)
<= (小于等于)
~ (匹配于)
!~ (不匹配)
! (非)
&& (与)
|| (或)
- 示例
# awk '/root/{print $1}' passwd 使用普通字符定位
# awk '$0 ~ /^root/ {print $1}' passwd 使用正则表达式定位
注意:正则需要用"/xx/"包住
从第一行开始匹配到以lp开头行
awk -F: 'NR==1,/^lp/{print $0 }' passwd
从第一行到第5行
awk -F: 'NR==1,NR==5{print $0 }' passwd
从以lp开头的行匹配到第10行
awk -F: '/^lp/,NR==10 {print $0 }' passwd
从以root开头的行匹配到以lp开头的行
awk -F: '/^root/,/^lp/{print $0}' passwd
打印以root开头或者以lp开头的行
awk -F: '/^root/ || /^lp/{print $0}' passwd
awk -F: '/^root/;/^lp/{print $0}' passwd
显示5-10行
awk -F':' 'NR>=5 && NR<=10 {print $0}' /etc/passwd
awk -F: 'NR<10 && NR>5 {print $0}' passwd
//打印30-39行以bash结尾的内容:
[[email protected] shell05]# awk 'NR>=30 && NR<=39 && $0 ~ /bash$/{print $0}' passwd
stu1:x:500:500::/home/stu1:/bin/bash
yunwei:x:501:501::/home/yunwei:/bin/bash
user01:x:502:502::/home/user01:/bin/bash
user02:x:503:503::/home/user02:/bin/bash
user03:x:504:504::/home/user03:/bin/bash
打印IP地址
# ifconfig eth0|awk 'NR>1 {print $2}'|awk -F':' 'NR<2 {print $2}'
# ifconfig eth0|grep Bcast|awk -F':' '{print $2}'|awk '{print $1}'
# ifconfig eth0|grep Bcast|awk '{print $2}'|awk -F: '{print $2}'
# ifconfig eth0|awk NR==2|awk -F '[ :]+' '{print $4RS$6RS$8}'
# ifconfig eth0|awk "-F[ :]+" '/inet addr:/{print $4}'
[[email protected] shell05]# ifconfig eth0|awk 'NR==2{print $0}'| awk -F'[ :]+' '{print $4 ORS $6 ORS $8}'
10.1.1.1
10.1.1.255
255.255.255.0
[[email protected] shell05]# ifconfig eth0|awk 'NR==2{print $0}'
inet addr:10.1.1.1 Bcast:10.1.1.255 Mask:255.255.255.0
[[email protected] shell05]# ifconfig eth0|awk -F'[ :]+' 'NR==2{print $4 ORS $6 ORS $8}'
10.1.1.1
10.1.1.255
255.255.255.0
5. 课堂练习
1、显示可以登录操作系统的用户所有信息 //从第7列匹配以bash结尾,输出整行(当前行所有的列)
awk '/bash$/{print $0}' /etc/passwd
1042 awk '/bash$/{print $0}' /etc/passwd
1043 awk '/bash$/' /etc/passwd
1044 awk -F: '$7 ~ /bash/' /etc/passwd
1045 awk -F: '$NF ~ /bash/' /etc/passwd
1046 awk -F: '$0 ~ /bash/' /etc/passwd
1047 awk -F: '$0 ~ /\/bin\/bash/' /etc/passwd
2、显示可以登录系统的用户名
# awk -F: '$0 ~ /\/bin\/bash/{print $1}' /etc/passwd
3、打印出系统中普通用户的UID和用户名如下显示:
# awk -F: 'BEGIN{print "UID\tUSERNAME"} {if($3>=500 && $3 !=65534 ) {print $3"\t"$1} }' /etc/passwdUID USERNAME
500 stu1
501 yunwei
502 user01
503 user02
504 user03
4、以数字开头
awk '/^[0-9]/{print $0}' 1.txt
5、以任意大小写字母开头
awk '/^[a-Z]/{print $0}' 1.txt
6. awk的脚本编程
5.1 流程控制语句
if语句:
if [ xxx ];then
xxx
fi
格式:
{if(表达式){语句;语句;...}}
[[email protected] shell05]# awk -F: '{if($3==0) {print $1"是管理员"} }' passwd
root是管理员
if...else语句:
if [ xxx ];then
xxxxx
else
xxx
fi
格式:
{ if(表达式){语句;语句;...}else{语句;语句;...} }
# awk -F: '{ if($3>=500 && $3 != 65534) {print $1"是普通用户"} else {print $1"不是普通用户"}}' passwd
if...else if...else语句:
if [xxxx];then
xxxx
elif [xxx];then
xxx
....
else
...
fi
格式:
{if(表达式1){语句;语句;...}else if(表达式2){语句;语句;...}else if(表达式3){语句;语句;...}else{语句;语句;...}}
# awk -F: '{ if($3==0) {print $1":是管理员"} else if($3>=500 && $3!=65534) {print $1":是普通用户"} else {print $1":是系统用户"}}' passwd
awk -F: '{if($3==0){i++} else if($3>=500){k++} else{j++}} END{print i; print k; print j}' /etc/passwd
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd
5.2 循环语句
while:
# i=1;while (( $i <=10 ));do echo $i;let i++;done
# awk 'BEGIN{i=1; while(i<=10){print i; i++}}' //打印1-10
# awk -F: '{i=1; while(i<=10) {print $0;i++}}' /etc/passwd //将每行打印10次
for:
# awk 'BEGIN{for(i=1;i<=5;i++){print i} }' //C风格for
1
2
3
4
5
# awk -F: '{for(i=1;i<=10;i++) print $0}' /etc/passwd //将每行打印10次
for
打印1~5
# awk 'BEGIN{for(i=1;i<=5;i++) print i}'
1
2
3
4
5
打印1~10中的奇数
# awk 'BEGIN{for(i=1;i<=10;i=i+2) print i}'
1
3
5
7
9
while
打印1~5
# awk 'BEGIN {i=1;while(i<=5) {print i;i++}}'
打印1~10中的奇数
# awk 'BEGIN {i=1;while(i<=10) {print i;i+=2}}'
1
3
5
7
9
# awk 'BEGIN{for(y=1;y<=5;y++){for(x=1;x<=y;x++)printf x;print}}'
1
12
123
1234
12345
# awk 'BEGIN{y=1;while(y<=5) { for(x=1;x<=y;x++) {printf x} y++ ;print } }'
1
12
123
1234
12345
尝试用三种方法打印99口诀表:
# awk 'BEGIN{for(y=1;y<=9;y++) { for(x=1;x<=y;x++) printf x"*"y"="x*y"\t";print} }'
# awk 'BEGIN{i=1;while(i<=9){for(j=1;j<=i;j++){printf j"*"i"="j*i"\t"}print;i++ }}'
# awk 'BEGIN{for(i=1;i<=9;i++){j=1;while(j<=i){printf j"*"i"="i*j"\t";j++};print}}'
循环的控制:
break 条件满足的时候中断循环
continue 条件满足的时候跳过循环
# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3) break;print i}}'
1
2
# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3) continue;print i}}'
1
2
4
5
# awk 'BEGIN{i=1;while(i<=5){if(i==3) break;print i;i++}}'
1
2
# awk 'BEGIN{i=0;while(i<5){i++;if(i==3) continue;print i}}'
1
2
4
5
5.3 算数运算
+ - * / %(模) ^(幂2^3)
可以在模式中执行计算,awk都将按浮点数方式执行算术运算
# awk 'BEGIN{print 1+1}'
# awk 'BEGIN{print 1**1}'
# awk 'BEGIN{print 2**3}'
# awk 'BEGIN{print 2/3}'
####7. 统计案例
1. 统计/etc/passwd 中各种类型shell的数量
# awk -F: '{shells[$NF]++};END{for (i in shells){print i,shells[i]}}' /etc/passwd
shells[/bin/bash]++
shells[sbin/nologin]++
2. 网站访问状态统计 <当前时实状态 netstat>
# ss -antp|grep 80|awk '{states[$1]++};END{for(i in states){print i,states[i]}}'
TIME_WAIT 578
ESTABLISHED 1
LISTEN 1
# ss -an |grep :80 |awk '{states[$2]++} END{for(i in states){print i,states[i]}}'
LISTEN 1
ESTAB 5
TIME-WAIT 25
# ss -an |grep :80 |awk '{states[$2]++} END{for(i in states){print i,states[i]}}' |sort -k2 -rn
TIME-WAIT 18
ESTAB 8
LISTEN 1
3. 统计当前访问的每个IP的数量 <当前时实状态 netstat,ss>
# netstat -ant |grep :80 |awk -F: '{ip_count[$8]++} END{for(i in ip_count){print i,ip_count[i]} }' |sort
# ss -an |grep :80 |awk -F":" '!/LISTEN/{ip_count[$(NF-1)]++} END{for(i in ip_count){print i,ip_count[i]}}' |sort -k2 -rn |head
4. 统计Apache/Nginx日志中某一天的PV量 <统计日志>
# grep '27/Jul/2017' mysqladmin.cc-access_log |wc -l
14519
5. 统计Apache/Nginx日志中某一天不同IP的访问量 <统计日志>
# grep '27/Jul/2017' mysqladmin.cc-access_log |awk '{ips[$1]++} END{for(i in ips){print i,ips[i]} }' |sort -k2 -rn |head
# grep '07/Aug/2017' access.log |awk '{ips[$1]++} END{for(i in ips){print i,ips[i]} }' |awk '$2>100' |sort -k2 -rn
名词引入:
名词:VV = Visit View(访问次数)
说明:从访客来到您网站到最终关闭网站的所有页面离开,计为1次访问。若访客连续30分钟没有新开和刷新页面,或者访客关闭了浏览器,则被计算为本次访问结束。
独立访客(UV)
名词:UV= Unique Visitor(独立访客数)
说明:1天内相同的访客多次访问您的网站只计算1个UV。
网站浏览量(PV)
名词:PV=PageView (网站浏览量)
说明:指页面的浏览次数,用以衡量网站用户访问的网页数量。多次打开同一页面则浏览量累计。用户每打开一个页面便记录1次PV。
独立IP(IP)
名词:IP=独立IP数
说明:指1天内使用不同IP地址的用户访问网站的数量。同一IP无论访问了几个页面,独立IP数均为1