12. Shell-三剑客

sed

功能简介

sed的用途

sed是Stream Editor(流编辑器)的缩写,简称流编辑器;是用于处理文件的。

sed如何处理文件

sed是一行一行读取文件内容,并按照要求进行处理,把处理后的结果输出到屏幕。

  • 示意图

  • 原理
  1. 首先sed读取文件中的一行内容,把其保持在一个临时缓存区中(也称为模式空间)。
  2. 然后根据需求处理临时缓冲区中的行,完成后把该行发送到屏幕上。
  • 总结
  1. 由于sed把每一行都保存在临时缓冲区中,对这个副本进行编辑,所以不会直接修改原文件
  2. sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,对文件进行过滤和转换操作

使用方法–命令行格式

sed常见的语法格式有两种,一种叫命令行模式,另一种叫脚本模式。

语法格式

sed [选项] '处理动作' 文件名

常用选项

选项说明备注
-e进行多项(多次)编辑
-n取消默认输出不自动打印模式空间
-r使用扩展正则表达式
-i原地编辑(修改源文件)
-f指定sed脚本的文件名

常见处理动作

动作说明备注
‘p’打印
‘i’在指定行之前插入内容类似vim中的大写O
‘a’在指定行之后插入内容类型vim中的小写o
‘c’替换指定行的所有内容
‘d’删除指定行

常见定位

符号说明举例
$最后一行
1第一行
1,3第1到3行

举例说明

准备文件

cp /etc/passwd /tmp/test

格式

#格式
sed 选项 '定位+命令' 需要处理的文件
  1. 打印文件内容。
  • p:打印内容
  • -n:取消默认输出
sed -n 'p' /tmp/test			#打印每一行,取消默认输出
sed -n '1p' /tmp/test			#打印第1行
sed -n '2p' /tmp/test			#打印第2行
sed -n '1,5p' /tmp/test		#打印1到5行
sed -n '$p' /tmp/test			#打印最后1行
  1. 增加文件内容。
  • i:在地址定位的上面插入
  • a:在地址定位的下面插入
  • -n:取消默认输出
sed '$awang' /tmp/test							#文件最后一行下面增加内容
sed 'awang' /tmp/test								#文件的每一行下面都增加内容
sed '5awang' /tmp/test							#文件的第5行下面增加内容
sed '$iwang' /tmp/test							#文件最后一行上面增加内容
sed 'iwang' /tmp/test								#文件的每一行上面都增加内容
sed '6iwang' /tmp/test							#文件的第6行上面增加内容
sed '/^uucp/ihello' /tmp/test				#以uucp开头行的上一行插入内容
sed '/^uucp/ahello' /tmp/test				#以uucp开头行的下一行插入内容
  1. 修改文件内容。
  • c:在地址定位处替换
sed '5chello world' /tmp/test				#替换文件第5行内容
sed 'chello world' /tmp/test				#替换文件所有内容
sed '1,5chello world' /tmp/test			#替换文件1到5行内容为hello world
sed '^/user01/c88888' /tmp/test			#替换以user01开头的行
  1. 删除文件内容。
  • d:在地址定位处删除
sed '1d' /tmp/test				#删除文件第1行
sed '1,5d' /tmp/test			#删除文件第1到5行
sed '$d' /tmp/test				#删除文件最后一行
  1. 搜索文件内容。
  • s表示搜索;斜杠(/)表示分割符,可以自定义;动作一般是打印(p)或全局替换(g)。
# 格式
sed 's/搜索的内容/替换的内容/动作' 需要处理的文件

sed -n 's/root/ROOT/p' /tmp/test									#搜索root,替换为ROOT并打印
sed -n 's/root/ROOT/gp' /tmp/test									#全局搜索root,替换为ROOT并打印
sed -n 's/\/sbin\/nologin/wang/gp' /tmp/test			#全局搜索/sbin/nologin,替换为wang并打印(使用\转义特殊字符)
sed -n 's@/sbin/nologin@wang@gp' /tmp/test				#自定义分割符,全局搜索/sbin/nologin,替换为wang并打印
sed -n '1,5s@/sbin/nologin@wang@p' /tmp/test			#自定义分割符,1到5行搜索/sbin/nologin,替换为wang并打印
sed -n '6,8s/^/#/p' /tmp/test											#注释6到8行内容
sed -n '1,10s/^#//gp' /tmp/test										#取消1到10行的注释
  1. 其他命令
  • 命令列表
    | 命令 | 解释 | 备注 |
    | — | — | — |
    | r | 从另外文件中读取内容 | |
    | w | 内容另存为 | |
    | & | 保存查找串以便在替换串中引用 | \(\) |
    | = | 打印行号 | |
    | ! | 对所选行以外的所有行应用命令,放到行数之后 | |
    | q | 退出 | |

  • 举例

sed '3r /etc/hosts' /tmp/test							#在/tmp/test的第3行下读取/etc/hosts文件的内容
sed '$r /etc/hosts' /tmp/test							#在/tmp/test的最后1行下读取/etc/hosts文件的内容

sed '1,3w /tmp/test.bak' /tmp/test				#将/tmp/test的1到3行保存到/tmp/test.bak文件中
sed '$w /tmp/test.bak' /tmp/test					#将/tmp/test的最后一行行保存到/tmp/test.bak文件中

sed -n 's/^sync/#&/gp' /tmp/test					#将/tmp/test的sync开头的前边添加#
sed -n 's/\(^rsync\)/\#1/gp' /tmp/test		#将/tmp/test的sync开头的前边添加#,1表示匹配到的sync

sed -ne '/root/=' -ne '/root/p' /tmp/test	#打印/tmp/test文件中包含root关键字的行号及行内容
sed -ne '/root/p' -ne '/root/=' /tmp/test	#打印/tmp/test文件中包含root关键字的行号及行内容

sed -n '1,5p' /tmp/test										#打印/tmp/test文件中1到5行
sed -n '1,5!p' /tmp/test									#不打印/tmp/test文件中1到5行

sed '/shutdown/q' /tmp/test								#打印/tmp/test文件中的内容,直到遇到shutdown关键字后退出
  1. 其他选项
  • -e
在/tmp/test文件中的第5行的前面插入“hello word”;
在/tmp/test文件中的第8行下面插入“哈哈哈”;
并打印行号;
sed -e '5ihello word' -e '8a哈哈哈' -e '5=;8=' /tmp/test

过滤/etc/ssh/sshd_config中以“#”开头和空行
sed -e '/^#/d' -e '/^$/d' /etc/ssh/sshd_config

  • -r
过滤/etc/ssh/sshd_config中以“#”开头和空行
sed -r '/^#|^$/d' /etc/ssh/sshd_config

过滤/etc/ssh/sshd_config中生效的行
sed -r '/^(#|$|;|\t#|\t$)/d' /etc/ssh/sshd_config
  • -i
注意:选项n和i不能在一起使用;选项i不能和动作p一起使用;
sed -i '1,5s/^/#&' /etc/ssh/sshd_config
sed -i '17{s/YUNWEI/yunwei/;s#/bin/bash#/sbin/nologin#}'
  1. 结合正则使用
    | 正则 | 说明 | 备注 |
    | — | — | — |
    | /key/ | 查询包含关键字的行 | sed -n ‘/root/p’ /tmp/test |
    | /key1/,/key2/ | 匹配包含两个关键字之间的行 | sed -n ’ /adm/,/mysql/p’ /tmp/test |
    | /key/,x | 从匹配关键字的行开始,到文件第x行之间的行(包含关键字所在行) | sed -n ‘/^ftp/,7p’ /tmp/test |
    | x,/key/ | 从第x行开始到关键字的匹配行之间的行 | sed -n ‘7,/^ftp/p’ /tmp/test |
    | x,y! | 不包含x到y行 | sed -n ‘3,7!p’ /tmp/test |
    | /key/! | 不包含关键字的行 | sed -n ‘/ftp/!p’ /tmp/test |

使用方法–脚本格式

用法

  • 使用
#方法一
#sed -f sed命令脚本 要操作的文件
sed -f sed_script.sh /tmp/test

#方法二
#./sed命令脚本 要操作的文件
chmod a+x sed_script.sh
./sed_script.sh /tmp/test
  • sed命令脚本
#!/bin/sed -f

1,5d
s/root/hello/g
3i777
5i888
p

注意事项

  1. 脚本文件是一个sed的命令行清单。
  2. 在每行的末尾不能有任何空格、制表符或其他文本。
  3. 如果在一行中有多个命令,那么使用分号(;)分隔。
  4. 不需要且不可用引号包含命令。
  5. #号开头的为注释行。

awk

功能介绍

awk介绍

awk是一种编程语言,主要用于Linux或Unix下对文本和数据的处理。处理的数据可以是来自标准输入、一个或多个文件、其他命令输出。
awk的处理文件和数据的方式:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行想要的操作。

awk用途

  1. awk用来处理文件和数据,是Linux/Unix下的一个工具,也是一种编程语言。
  2. awk可以用来统计数据,比如网站的访问量、访问的IP量等。
  3. 支持条件判断,支持for和while循环。

awk原理

  1. awk使用一行作为输入,并将这一行赋值给内部变量$0,每一行也可以称为一个记录,以换行符(RS)结束。
  2. 每行被间隔符“:”(默认为空格或制表符)分解成字段(或域),每个字段存储再已经编号的变量中,从$1开始。
    1. awk如何知道用空格来分隔字段呢?
    2. 因为有一个内部变量FS来确定字段分隔符,初始时,FS赋值为空格。
  3. awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1与$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量即输出字段分隔符OFS,OFS默认为空格。
  4. awk处理完一行后,将从文件中获取另一行,并将其存储再$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕。

使用方式–命令模式

语法格式

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'
  • BEGIN…END…
'BEGIN{awk语句};{处理中};END{awk语句}'
'BEGIN{awk语句};{处理中}'
'{处理中};END{awk语句}'

使用方式–脚本模式

用法

  • 使用
#方法一
#awk 选项 -f awk命令脚本 要处理的文本文件
awk -f awk_script.sh /tmp/test

#方法二
#./awk命令脚本 要处理的文本文件
chmod a+x awk_script.sh
./awk_script.sh /tmp/test
  • awk命令脚本
#!bin/awk -f

BEGIN{FS=":"}
NR==1,NR==3{print $1"\t"$NF}

注意事项

  1. 脚本文件是一个sed的命令行清单。
  2. 在每行的末尾不能有任何空格、制表符或其他文本。
  3. 如果在一行中有多个命令,那么使用分号(;)分隔。
  4. 不需要且不可用引号包含命令。
  5. #号开头的为注释行。

相关变量

常用内置变量和常用分隔符

变量变量说明备注
$0当前处理行的所有记录
$1,$2, 3 , . . . , 3,..., 3,...,n文件中每行以间隔符号分割为不同字段awk -F: ‘{print $1,$3}’
NF当前记录的字段数(列数)awk -F: ‘{print NF}’
$NF当前记录的最后一列字段awk -F: ‘{print $NF}’
$(NF-1)当前记录的倒数第二列字段awk -F: ‘{print $(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当前输入的文件名

变量使用举例

  • 打印所有内容
awk '{print $0}' /etc/passwd
  • 打印1到5行
awk 'NR==1,NR==5{print $0}' /etc/passwd
  • 打印第1行或者第5行
awk 'NR==1 || NR==5{print $0}' /etc/passwd
  • 打印3到5行
awk 'NR>=3 && NR<=5{print $0}' /etc/passwd
  • 打印列数
awk -F: '{print NF}' /etc/passwd
  • 打印第1列和最后一列
awk -F: '{print $1,$NF}' /etc/passwd
  • 打印第3列和倒数第2列
awk -F: '{print $3,$(NF-1)}' /etc/passwd
  • 打印第1,3,5,倒数第二列
awk -F: '{print $1,$3,$5,$(NF-1)}' /etc/passwd

分隔符使用举例

  • 自定义分隔符,并自定义输出分隔符
awk -F: 'BEGIN{OFS="#####"};{print $1,$NF}' /etc/passwd
  • 自定义分隔符,并自定义输出分隔符
awk 'BEGIN{FS=":";OFS="#####"};{print $1,$NF}' /etc/passwd
  • 自定义分隔符,并自定义输出分隔符
awk 'BEGIN{FS=":"};{print $1"######"$NF}' /etc/passwd

相关进阶

print和pringf

print和printf都是格式化输出命令。print类似echo(自带换行功能);printf类似echo -n(取消换行功能)。

  • print举例
date
date | awk '{print "月份: " $2 "\n年份:" $NF}'

cat /etc/passwd
awk -F: '{print "用户名:" $1 "\t UID号是:" $3}' /etc/passwd
  • printf举例
#printf常用符号
%s			#字符串类型				%-20s:左对齐,占20个字符的字符串类型
%d			#数值类型					%-10d:左对齐,占10个字符的数值类型
-				#表示左对齐,默认是右对齐
printf默认不会在行尾自动换行,如需换行需要加\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 | %1-5s|\n", %1,$2,$3}' /etc/passwd
awk 'BEGIN{FS=":"};{printf "%-15s %-15s %-15s\n", $1,$6,$NF}' /etc/passwd

定义变量

在awk中可以定义变量,调用变量时无需使用“$”直接写变量名即可调用。

awk -v NUMBER=3 -F: '{print NUMBER}' /etc/passwd
awk -v NUBMER=3 'BEGIN{print NUMBER}'

BEGIN…END…

  • 解释
  1. BEGIN:表示在程序开始前执行
  2. END:表示所有文件处理完成后执行
  3. 用法:awk 选项 ‘BEGIN{开始处理之前};{处理中};END{处理结束后}’ 要处理的文件
  • 举例
#打印最后一列和倒数第二列
#举例一:
awk -F: 'BEGIN{print "Login_shell\t\tLogin_home\n********************"};{print $NF"\t\t"$(NF-1)};END{print "********************"}' /etc/passwd
#举例二:
awk 'BEGIN{FS=":";print "Login_shell\t\tLogin_home\n********************"};{print $NF"\t\t"$(NF-1)};END{print "********************"}' /etc/passwd

#打印/etc/passwd里的用户名、家目录及登录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 "********************"}' /etc/passwd

正则表达式

  • 运算符列表
    | 运算符 | 说明 |
    | — | — |
    | == | 等于 |
    | != | 不等于 |
    | > | 大于 |
    | < | 小于 |
    | >= | 大于等于 |
    | <= | 小于等于 |
    | ~ | 匹配 |
    | !~ | 不匹配 |
    | ! | 逻辑非 |
    | && | 逻辑与 |
    | || | 逻辑或 |

  • 举例

#从第一行开始匹配,到以lp开头行截止
awk -F: 'NR==1,/^lp/{print $0}' /etc/passwd
#打印第一行到第五行
awk -F: 'NR==1,NR==5{print $0}' /etc/passwd
#从以lp开头的行匹配到第10行
awk -F: '/^lp/,NR=10{print $0}' /etc/passwd
#从以root开头的行匹配到以lp开头的行
awk -F: '/^root/,/^lp/{print $0}' /etc/passwd
#匹配以root开头或者以lp开头的行
awk -F: '/^root/ || /^lp/{print $0}' /etc/passwd
awk -F: '/^root/;/^lp/{print $0}' /etc/passwd
#显示5-10行
awk -F: 'NR<10 && NR>5 {print $0}' /etc/passwd
awk -F':' 'NR>=5 && NR<=10 {print $0}' /etc/passwd
#打印30-39行以bash结尾的内容
awk 'NR>=30 && NR<=39 && $0 ~ /bash$/{print $0}' /etc/passwd
#打印30-39行不匹配以bash结尾的内容
awk 'NR>=30 && NR<=39 && $0 !~ /bash$/{print $0}' /etc/passwd
  • 综合
#打印IP地址
ifconfig ens32 | awk 'NR==2 {print $0}' | awk -F' ' '{print $2}'
ip addr | awk '/ens32/ && /inet/ {print $0}' | awk -F'[/ ]+' '{print $3}'
ip addr | awk -F'[/ ]+' '/ens32/ && /inet/ {print $3}'
#解释
#[/ :]+表示匹配间隔符“:”、“/”和空格,并可以出现多次
#[]中可以定义多个分隔符;+表示多次重复出现;

流程控制

  • if…
#格式:
awk 选项 '{if(判断表达式) {处理语句1;处理语句2;...}}' 文件名

#举例:
awk -F: '{if($3>=500 && $3<=60000) {print $1,$3}}' /etc/passwd
awk -F: '{if($3==0) {print $1"是管理员"}}' /etc/passwd
awk -F: 'BEGIN{if($(id -u)==0) {print "admin"}}' /etc/passwd
  • if…else…
#格式:
awk 选项 '{if(表达式) {语句1;语句2;...} else {语句1;语句2;...}}'

#举例:
awk -F: '{if($3>=500 && $3 != 65534) {print $1"是普通用户"} else {print $1"不是普通用户"}}'
awk -F: '{if($(id -u)>=500 && $(id -u) != 65534) {print $1"是普通用户"} else {print $1"不是普通用户"}}'
  • if…else if…else
#格式:
awk 选项 '{if(表达式1) {语句1-1;语句1-2;...} else if(表达式2) {语句2-1;语句2-1;...} else {语句3-1;语句3-2;...}}'

#举例:
awk -F: '{if($3==0) {print $1"是管理员"} else if($3>=1 && $3<=499 || $3==65534) {print $1"是系统用户"} else {print $1"是普通用户"}}'
awk -F: '{if($3==0) {i++} else if($3>=1 && $3<=499 || $3==65534) {j++} else {k++}};END{print "管理员个数:"i "\n系统用户个数为:"j "\n普通用户个数为:"k}'

循环结构

  • for
#格式:
awk 'BEGIN {变量;for(表达式) {语句}}'

#举例:
awk 'BEGIN {for(i=1;i<=5;i++) {print i}}'
awk 'BEGIN {for(i=1;i<=5;i++) (sum+=i);{print sum}}'
awk 'BEGIN {for(i=1;i<=5;i++) (sum+=i);print sum}'
awk 'BEGIN {sum=0;for(i=1;i<=5;i++) sum+=i;print sum}'
awk 'BEGIN {for(i=1;i<=10;i+=2) print i}'
awk 'BEGIN {for(i=1;i<=10;i+=2) {print i}}'
  • while
#格式:
awk 'BEGIN {变量;while(表达式) {语句}}'

#举例:
awk 'BEGIN {i=1;while(i<=5) {print i; i++}}'
awk 'BEGIN {i=1;while(i<=10) {print i; i+=2}}'
awk 'BEGIN {i=1;sum=0;while(i<=5) {sum+=i; i++}; print sum}'
awk 'BEGIN {i=1;while(i<=5) {(sum+=i) i++}; print sum}'
  • 嵌套
#格式:
awk 'BEGIN {for(表达式) {for(表达式) {语句}};语句}'
awk 'BEGIN {变量;while(表达式) {变量;while(表达式) {语句}};语句}'
awk 'BEGIN {变量;while(表达式) {for(表达式) {语句}}}'

#举例:
awk 'BEGIN {for(y=1;y<=5;y++) {for(x=1;x<=y;x++) {printf x};print}}'
awk 'BEGIN {y=1;while(y<=5) {for(x=1;x<=y;x++) {printf x};y++;print}}'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值