文章目录
1、sed的简单介绍
sed是流编辑器,用于处理文件
sed是一行一行读取文件内容并按照要求进行处理,把处理后的结果输出到屏幕
1、首先sed读取文件中的一行内容,把其保存在一个临时缓存区中(也称为模式空间)
2、然后根据需求处理临时缓冲区中的行,完成后把该行发送到屏幕上
总结:
1.由于sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会直接修改原文件
2.Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作,对文件进行过滤和转换操作
2、sed的使用方法
sed常见的语法格式有两种,一种叫命令行模式,另一种叫脚本模式。
2.1 命令行格式
语法格式
sed [options] '处理动作' 文件名
常见选项
选项 | 说明 | 备注 |
---|---|---|
-e | 进行多项(多次)编辑 | 直接在命令列模式上进行 sed 的动作编辑 |
-n | 取消默认输出 | 在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。 |
-r | 使用扩展正则表达式 | sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法) |
-i | 原地编辑(修改源文件) | 直接修改读取的文件内容,而不是输出到终端 |
-f | 指定sed脚本的文件名 | 直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作 |
常见的处理动作
以下所有的动作都要在单引号里
动作 | 说明 | 备注 |
---|---|---|
‘p’ | 打印 | |
‘i’ | 在指定行之前插入内容 | 类似vim里的大写O |
‘a’ | 在指定行之后插入内容 | 类似vim里的小写o |
‘c’ | 替换指定行所有内容 | |
‘d’ | 删除指定行 |
2.2 案例
文件准备
#a.txt
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4: adm:/var/adm:/sbin/nologin
lp:x:4:7:tp:/var/spool/lpd:/sbin/nologin
298374837483
172.16.0.254
10.1.1.1
打印文件内容
sed '' a.txt #对文件什么都不做,但是由于没有-n参数,因此还会将文件的内容打印到屏幕
sed -n 'p' a.txt #打印每一行,并取消默认输出
sed -n '1p' a.txt #打印第一行
sed -n '2p' a.txt #打印第二行
sed -n '1,5p' a.txt #打印1-5行
sed -n '$p' a.txt #打印最后一行
增加文件内容(单独成行)
sed '$a99999' a.txt #文件最后一行后面增加内容
sed 'a99999' a.txt #文件每行后面增加内容
sed '5a99999' a.txt #文件第5行后面增加内容
sed '$i99999' a.txt #文件最后一行的前面增加内容
sed 'i99999' a.txt #文件每行的前一行增加内容
sed '6i99999' a.txt #文件第6行前一行增加内容
sed '/^uucp/ihello' #以uucp开头行的前一行插入内容
#在第1行前连续插入3行内容(第1中写法只会将要插入的内容看成一行,应该遵循第2种写法)
sed -n '1i\hello\world\888' a.txt
结果:
helloworld888
sed '1i\
> hello\
> world\
> fl' a.txt
结果:
hello
world
fl
sed '2,4a999' a.txt #在2-4行的每一行后面一行都增加内容
修改文件内容
sed '5chello world' a.txt #替换文件第5行的内容
sed 'chello world' a.txt #替换文件所以内容
sed '1,5chello world' a.txt #替换文件1到5行内容为hello world,不是1到5行每一行都替换
sed '/^user01/c888888' a.txx #替换user01开头的行
sed如果需要结合正则,需要将正则表达式用斜杠包裹起来,例如:
sed '/^adm/chello fl' a.txt
删除文件内容
sed '1d' a.txt #删除文件第一行
sed '1,5d' a.txt #删除文件1到5行
sed '$d' a.txt #删除文件最后一行
sed '/[0-9]/d' a.txt #删除包含数字的行
sed -r '/([0-9]\.){1,3}[0-9]{1,3}/d' a.txt #删除文件中的ip地址,-r表示支持扩展正则
对文件进行搜索替换操作
语法:sed 选项 's/搜索内容/替换内容/动作' 需要处理的文件
其中,s表示search搜索。斜杠/表示分隔符,可以自定义。动作一般是打印p和全局替换g。如果不加g,一行中有多个匹配到了,只会替换第一个,加g,则全部替换
sed -n 's/root/ROOT/gp' a.txt #全文搜索root并替换成ROOT
sed -n 's/^#//gp' a.txt #删除首行以#开头的#,相当于取消注释
sed -n 's@/sbin/nologin@fl@gp' a.txt #将全文中的/sbin/nologin替换为fl
或者写为:sed -n 's/\/sbin\/nologin@fl@gp' a.txt
sed -n '10s@/sbin/nologin@fl@gp' a.txt #将第10行中的/sbin/nologin替换为fl,如果没有,就不处理
sed -n '1,5s/^/#/gp' a.txt #注释前5行
sed -n 's#\(10.1.1.\)1#\1fl#gp' a.txt #(10.1.1.)表示\1,把最后一个1替换为fl
其他命令
命令 | 含义 |
---|---|
r | 从另外文件读取内容 |
w | 内容另存为 |
& | 保存查找串以便在替换串中引用 |
= | 打印行号 |
! | 对所选行以外的所有行应用命令,放到行数之后 |
q | 退出 |
sed '3r /etc/hosts' a.txt #将/etc/hosts中的数据读到a.txt的第3行之后的位置
[root@fl Shell]# sed '3r /etc/hosts' a.txt
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
127.0.0.1 fl fl
adm:x:3:4: adm:/var/adm:/sbin/nologin
lp:x:4:7:tp:/var/spool/lpd:/sbin/nologin
298374837483
172.16.0.254
10.1.1.1
sed '1,5w b.txt' a.txt #将a.txt中的1-5行保存到b.txt中
sed -n 's/^lp/#&/gp' a.txt #在以lp开头的前面添加#号 &和\(\)的作用相同
#lp:x:4:7:tp:/var/spool/lpd:/sbin/nologin
sed -n 's/^lp/&#/gp' a.txt #在以lp开头的后面添加#号
lp#:x:4:7:tp:/var/spool/lpd:/sbin/nologin
sed -n '1,5!p' a.txt #打印非1到5行
sed -ne '/root/p' a.txt -ne '/root/=' #打印root所在的行,并打印行号
sed -e '/^#/d' -e '/^$/d' a.txt #删除以#开头行(取消注释),删除空行
sed -r '/^#|^$/d' a.txt
2.3 sed结合正则使用
sed 选项 'sed’命令或者正则表达式或者地址定位文件名
1.地址用于决定对哪些行进行编辑。地址的形式可以是数字、正则表达式、或二者的合。
2.如果没有指定地址. sed将外理输入文件的所有行。
正则 | 说明 | 案例 |
---|---|---|
/key/ | 查询包含关键字的行 | sed -n ‘/root/p’ a.txt |
/key1/,/key2/ | 匹配包含两个关键字之间的行 | sed -n ‘/^adm/, /^mysql/p’ a.txt |
/key/,X | 从匹配关键字的行开始到文件第x行之间的行(包含关键字所在行) | sed -n ‘/^ftp/,7p’ |
x,/key/ | 从文件的第x行开始到与关键字的匹配行之间的行 | |
x,y! | 不包含x到y行 |
sed -nr '/^lp | ^mysql/p' a.txt #找出以lp或者mysql开头的行
sed -n '/sync/,8p' a.txt #打印sync所在的行到第8行之间所有的行,闭区间
sed -n '3,/^halt/p' a.txt #从第3行开始,打印以halt开头的行之间的所有内容,闭区间
2.4 脚本格式
用法
#sed -f scripts.sh file //使用脚本处理文件
建议使用 ./sed.sh file
脚本的第一行写上
#!/bin/sed -f
注意事项
脚本文件是一个sed的命令行清单。’ commands’
在每行的末尾不能有任何空格、制表符(tab)或其它文本。
如果在一行中有多个命令,应该用分号分隔。
不需要且不可用引号保护命令
#号开头的行为注释
3、awk的简单介绍
awk概述
awk是一种编程语言,主要用于在linux/unix下对文本和数据进行处理,是linux/unix 下的一个工具。数据可以来自标准输入、一个或多个文件,或其它命令的输出。
awk的处理文本和数据的方式:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
awk能干啥?
1、awk用来处理文件和数据的,是类unix下的一个工具,也是一种编程语言
2、可以用来统计数据,比如网站的访问量,访问的IP量等等
3、支持条件判断,支持for和while循环
4、awk的使用方法
4.1 命令行模式
语法结构
awk 选项 '命令部分' 文件名
特别说明:
引用Shell变量需要用双引号引起
常用选项介绍
- -F定义字段分隔符,默认分割符是空格
- -v定义变量并赋值
‘命名部分说明’
- 正则表达式,地址定位
'/root/{awk语句}' sed中: '/root/p'
'NR==1,NR==5{awk语句}' sed中: '1,5p'
'/^root/,/^ftp/{awk语句}' sed中: '/^root/,/^ftp/p'
- {awk语句1;awk语句2;…}
'{print $0;print $1}' sed中: 'p'
'NR==5{print $0}' sed中: '5p'
注: awk命令语句间用分号间隔
- BEGIN…END…
'BEGIN{awk语句};{处理中};END{awk语句}'
'BEGIN{awk语句};{处理中}'
'{处理中};END{awk语句}'
4.2 脚本模式
脚本编写
#!/bin/awk -f
一以下是awk引号里的命令清单,不要用引号保护命令,多个命令用分号间隔
BEGIN{FS=":"}
NR==1,NR==3{print $1"\t"$NF}
...
脚本处理
方法1:
awk 选项 -f awk的脚本文件 需要处理的文本文件
awk -f awk.sh filename
sed -f sed.sh -i filename
方法2:
./awk的脚本文件(或者绝对路径) 需要处理的文本文件
./awk.sh filename
./sed.sh filename
5、awk内部相关变量
变量 | 变量说明 | 备注 |
---|---|---|
$0 | 当前处理的所有记录 | |
$1,$2,$3…$n | 文件中每行以间隔符分割的不同字段,间隔符默认是空格 | awk -F:‘{print $1,$3}’ |
NF | 当前记录的字段数(列数) | awk -F:‘{print NF}’ |
$NF | 最后一列 | $(NF-1)表示倒数第二列 |
FNR/NR | 行号 | |
FS | 定义间隔符 | ‘BEGIN{FS=“:”};{print $1,$3}’ |
OFS | 定义输出字段分隔符,默认空格 | ‘BEGIN{OFS=“\t”};print $1,$3}’ |
RS | 输入记录分割符,默认换行 | ‘BEGIN{RS=“\t”};{print $0}’ |
ORS | 输出记录分割符,默认换行 | ‘BEGIN{ORS=“\n\n”};{print$1,$3}’ |
FILENAME | 当前输入的文件名 |
5.1 案例
文本准备
head /etc/passwd > 1.txt
tail -3 /etc/passwd >> 1.txt
[root@fl Shell]# cat 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
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
fl:x:1000:1000::/home/fl:/bin/bash
yunwei:x:1001:1001::/home/yunwei:/bin/bash
user1:x:1002:1002::/home/user1:/bin/bash
awk '{print $0}' 1.txt #打印每一行(因为没有分隔符,$0表示文本中的每一行)
awk 'NR==1,NR==5{print $0}' 1.txt #打印1-5行,同下
awk 'NR>=1 && NR<=5{print $0}' 1.txt
awk 'NR==1 || NR==5{print $0}' 1.txt #打印1和5行
awk -F: '{print $1,$(NF-1),$NF}' 1.txt #打印每一行以冒号分割的第一列,倒数第二列和最后一列
awk -F: '{print NF}' 1.txt #每一行以冒号分割,并打印每一行的列数
awk -F: '/root/{print $1,$NF}' 1.txt #打印包含root的,以冒号分割的第一行和最后一行
awk 'NR>=1 && NR<=5 && /^root/{print $0}' 1.txt #打印1-5行中以root开头的行
awk 'BEGIN{FS=":";OFS="@"};{print $1,$NF}' 1.txt #打印以冒号分割的第一列和最后一列,列与列之间用@作为分隔符,同下
awk 'BEGIN{FS=":";OFS="@"};{print $1"@"$NF}' 1.txt #分隔符要用双引号引起来
6、awk工作原理
awk -F: '{print $1,$3}' /ect/passwd
- awk使用一行作为输入,并将这一 行赋给内部交量50,每行也可称为一个记录,以换行符(RS)结束
- 每行被间隔符:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始
- awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格
- awk处理完一行后, 将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕
7、awk进阶使用
格式化的输出print和printf
print函数 类似echo
[root@fl Shell]# date | awk '{print "Month: "$2 "\nYear: "$NF}'
Month: Feb
Year: 2023
printf函数 类似echo -n
[root@fl Shell]# awk -F: 'NR==1,NR==3{printf "%-15s %-10s %-15s\n",$1,$2,$3}' /etc/passwd
root x 0
bin x 1
daemon x 2
%s 字符类型 strings
%d 数值类型
- 表示左对齐,默认是右对齐
%-15s 表示向左对齐15个字符,如果不够,用空格填充
printf默认不会在行尾自动换行,需要我们手动加上\n
awk变量定义
[root@fl Shell]# awk -v NUM=3 -F: 'NR==1{print NUM}' /etc/passwd
3
[root@fl Shell]# awk -v NUM=3 -F: 'NR==1{print $NUM}' /etc/passwd
0
[root@fl Shell]# awk -v num=1 'BEGIN{print num}'
1
[root@fl Shell]# awk -v num=1 'BEGIN{print $num}'
注意:
awk中调用定义的变量不需要加$,因为$+数字表示特定分隔符分割后的某一列
awk中BEGIN…END使用
BEGIN:表示在程序开始之前执行
END:表示所有文件处理完后执行
用法:‘BEGIN{开始处理之前};{处理中};END{处理结束后}’
案例1:打印最后一列和倒数第二列(登录shell和家目录)
[root@fl Shell]# awk -F: 'BEGIN{printf "Login_shell\tLogin_home\n********************\n"};{printf $NF"\t"$(NF-1)"\n"};END{printf "*******************\n"}' 1.txt
Login_shell Login_home
********************
/bin/bash /root
/sbin/nologin /bin
/sbin/nologin /sbin
/sbin/nologin /var/adm
/sbin/nologin /var/spool/lpd
/bin/sync /sbin
/sbin/shutdown /sbin
/sbin/halt /sbin
/sbin/nologin /var/spool/mail
/sbin/nologin /root
/bin/bash /home/fl
/bin/bash /home/yunwei
/bin/bash /home/user1
*******************
案例2:打印/etc/passwd里的用户名、家目录以及登录shell
[root@fl Shell]# awk -F: 'BEGIN{OFS="\t\t";print "u_name\t\th_dir\t\tshell\n********************"};{printf "%-20s %-20s %-20s\n",$1,$(NF-1),$NF};END{print "********************"}' 1.txt
u_name h_dir shell
********************
root /root /bin/bash
bin /bin /sbin/nologin
daemon /sbin /sbin/nologin
adm /var/adm /sbin/nologin
lp /var/spool/lpd /sbin/nologin
sync /sbin /bin/sync
shutdown /sbin /sbin/shutdown
halt /sbin /sbin/halt
mail /var/spool/mail /sbin/nologin
operator /root /sbin/nologin
fl /home/fl /bin/bash
yunwei /home/yunwei /bin/bash
user1 /home/user1 /bin/bash
********************
awk和正则的综合运用
这里只写了之前没有出现过的正则符号
运算符 | 说明 |
---|---|
~ | 匹配 |
!~ | 不匹配 |
案例
awk 'NR==1,/^lp/{print $0}' 1.txt #打印从第一行开始,到以lp开头的行中间所有的行,闭区间
awk '/^lp/,NR==10{print $0}' 1.txt #打印以lp开头的行,到第10行中间所有的行,闭区间
awk '/^root/ || /^lp/{print $0}' 1.txt #打印以root开头或者以lp开头的行
awk 'NR>=1 && NR <=5 && $0 ~ /bash$/{print $0}' 1.txt #打印1-5行中以bash结尾的行
案例3:打印IP地址
方法1:
[root@fl Shell]# ifconfig eth0 | awk 'NR==2{print $0}' | awk -F[' ']+ '{print $3}'
192.168.0.207
方法2:
[root@fl Shell]# ifconfig eth0 | grep -w 'inet' | awk -F[' ']+ '{print $3}'
192.168.0.207
方法3:
[root@fl Shell]# ifconfig eth0 | awk -F"[ ]+" '/inet/{print $3}' | awk 'NR==1{print $0}'
192.168.0.207
8、awk脚本编程
流程控制语句
if结构
格式:
awk 选项 '正则,地址定位{awk语句}' 文件名
{ if(表达式) {语句1;语句2;...}}
awk -F: '{if($3>=500 && $3<=60000) {print $1,$3} }' /etc/passwd
[root@fl Shell]# awk -F: '{if($3==0) {print $1"是管理员"} }' /etc/passwd
root是管理员
[root@fl Shell]# awk -F: 'BEGIN{if($(id -u)==0) {print "当前用户是admin"}}'
当前用户是admin
if…else结构
格式:
{if(表达式) {语句1;语句2;...} else {语句1;语句2;...}}
[root@fl Shell]# awk -F: 'NR==1,NR==3{if($3==0) {print $1"是管理员"} else {print $1"不是管理员"}}' /etc/passwd
root是管理员
bin不是管理员
daemon不是管理员
[root@fl Shell]# awk -F: 'BEGIN{if($(id -u)!=0) {print "当前用户不是admin"} else {print "当前用户是admin"}}'
当前用户是admin
if…else if…eles结构
格式:
{if(表达式1) {语句1;语句2;...} else if(表达式2) {语句1;语句2;...} else {语句1;语句2;...}}
[root@fl Shell]# awk -F: '{if($3==0) {print $1"是管理员"} else if($3>=1 && $3<=499 || $3==65534) {print $1"是系统 用户"} else {print $1"是普通用户"}}' 1.txt
root是管理员
bin是系统用户
daemon是系统用户
adm是系统用户
lp是系统用户
sync是系统用户
shutdown是系统用户
halt是系统用户
mail是系统用户
operator是系统用户
fl是普通用户
yunwei是普通用户
user1是普通用户
[root@fl Shell]# awk -F: '{if($3==0) {i++} else if($3>=1 && $3<=499 || $3==65534) {j++} else {k++}};END{print "管 理员的个数为:"i"\n系统用户的个数为:"j"\n普通用户的个数为"k}' 1.txt
管理员的个数为:1
系统用户的个数为:9
普通用户的个数为3
循环语句
打印1-5
awk 'BEGIN{for(i=1;i<=5;i++) {print i}}'
awk 'BEGIN{i=1;while(i<=5) {print i; i++}}'
打印1-10中的奇数
awk 'BEGIN{for(i=1;i<=10;i+=2) {print i}}'
awk 'BEGIN{i=1;while(i<=10) {print i; i+=2}}'
计算1-5的和
awk 'BEGIN{for(i=1;i<=5;i++) {sum+=i};{print sum}}'
awk 'BEGIN{i=1;while(i<=5) {sum+=i;i++};{print sum}}'
[root@fl Shell]# awk 'BEGIN{for(i=1;i<=5;i++) {for(j=1;j<=i;j++) {printf j};{print}}}'
1
12
123
1234
12345
8.1 案例
统计系统中各个类型的shell
[root@fl Shell]# awk -F: '{shells[$NF]++};END{for(i in shells) {print i"\t"shells[i]}}' /etc/passwd
/bin/sync 1
/bin/bash 4
/sbin/nologin 17
/sbin/halt 1
/sbin/shutdown 1
统计该服务器的所有访问状态
[root@fl Shell]# netstat -napt | grep '^tcp' | awk -F'[ ]+' '{states[$6]++};END{for(i in states) {print i":"states[i]}}'
LISTEN:5
ESTABLISHED:9
统计访问网站的每个IP的数量
ss -antp | grep 80 | awk -F: '!/LISTEN/{ip_count[$(NF-1)]++};END{for(i in ip_count) {print i":"ip_count[i]}}' | sort -k2 -rn | head