shell脚本编程
shell概述
在Linux内核与用户之间的解释器程序<br /> 通常指 /bin/bash,负责向内核翻译及传达用户/程序指令,相当于操作系统的“外壳”
shell的使用方式
- **交互式(命令行)**
人工干预、智能化程度高<br /> 逐条解释执行、效率低
- **非交互式(脚本)**<br />需提前设计、智能化难度大<br />批量执行、效率高<br />方便在后台悄悄的运行
常见的shell程序种类
[root@svr7 ~]# cat /etc/shells //查看所有解释器
/bin/sh //多数Unix默认的Shell
/bin/bash //多数Linux默认的Shell
/sbin/nologin //非登陆Shell
/bin/tcsh
/bin/csh
[root@svr7 ~]# sh //切换成sh解释器
sh-4.2# ls //利用sh解释器输入命令
sh-4.2#exit //退出sh解释器
[root@svr7 ~]# yum -y install ksh //安装新解释器
[root@svr7 ~]# ksh //进入新解释器
bash基本特性(优点)
shell脚本
提前写好可执行的语句,可以完成特定任务的文件按顺序、批量化执行
解释型程序
shell脚本编写规范
1,声明解释器
#!/bin/bash
2,编写注释
# 可以描述脚本功能、变量作用等信息
3,执行指令
ls、cd……
脚本执行的方式
1.作为命令字:为脚本添加x权限,使用相对或者绝对路径执行
chmod u+x test01.sh
./test01.sh 或者 /opt/test01.sh
用户(root)---bash---bash---echo
2.使用解释器程序执行脚本,无需x权限
bash test01.sh
用户(root)—-bash —-bash—-echo 会开启新的子进程
3.使用source命令执行脚本,无需x权限
source test01.sh
用户(root)—-bash—-echo 不会开启新的子进程
4.调试Shell脚本:sh -x 开启调试模式
简单的脚本示例
编写脚本,搭建yum
#!/bin/bash
# 搭建yum软件仓库
mkdir /abc
mount /dev/cdrom /abc
rm -rf /etc/yum.repos.d/*.repo
echo "[abc]
name=abcabc
baseurl=file:///abc
enabled=1
gpgcheck=0" > /etc/yum.repos.d/abc.repo
编写脚本,安装网站服务并开启,浏览网站时显示”web-test~~”
#!/bin/bash
# 搭建网站
yum -y install httpd
echo "web-test~~" > /var/www/html/index.html
systemctl restart httpd
编写脚本,安装ftp服务并开启且设置为开机自启
重定向标准输出
2> 重定向错误输出
&> 重定向所有输出#!/bin/bash yum -y install vsftpd &> /dev/null //将不需要的信息扔黑洞 systemctl restart vsftpd systemctl enable vsftpd
shell变量
变量和常量
变量 使用固定的名称存放可能发生变化的值,可以提高脚本的灵活度、适应力,方便重复使用
常量 固定不变的内容变量的设置和取消
- 定义/赋值变量
变量名=变量值 - 查看变量
引用变量值(调用变量):$变量名
查看变量:echo $变量名、echo ${变量名}
未定义的变量无取值;变量名以混淆时,以{}界定 取消变量
退出定义变量的Shell环境时,变量会自动失效
手动取消:unset 变量名、变量名=变量的种类
变量的分类角度
存储类型
整数型(int)、浮点型(float)、双精度浮点型(double)、字符型(char)、…….-
自定义变量 名称可以用数字、字母、下划线,不能以数字开头,不能使用特殊符号,
- 等号两边不能有空格,尽量不要使用关键字和特殊字符,
- 给同一个变量多次赋值时,最后一次的赋值生效
格式:变量名称=值a=10 //创建变量(对变量赋值),名字是a,值是10 a=30 //再次赋值,之前的会被覆盖 echo $a //调用变量时使用$符号 unset a //取消变量的定义 a= //将变量a赋值为空,效果同上 echo ${a}RMB //变量名容易与后续字符发生混淆时使用大括号隔开
环境变量:由系统提前定义好,使用时直接调用
配置文件:/etc/profile、~/.bash_profile
env:查看所有环境变量
set:查看所有变量
常见的环境变量:
USER 当前用户名 UID当前用户的id号 HOME当前用户家目录
HOSTNAME 主机名 SHELL当前用户的解释器 PWD当前位置
PATH 存储命令的路径 PS1 一级提示符 PS2 二级提示符预定义变量:用来保存脚本程序的执行信息,直接使用这些变量,不能赋值
$! | 后台运行的最后一个进程的进程号(PID) |
---|---|
$@ | 与$*相同,$@以”$1” … “$ 2”…”$n” 的形式输出所有参数 |
当 $* 和 $@ 不被双引号” “包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号” “包含时,就会有区别了:
- “$*”会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。
- “$@”仍然将每个参数都看作一份数据,彼此之间是独立的。
位置变量:在执行脚本时提供的命令行参数
表示为$n,n为序号;$1,$2,……..$10变量的拓展应用:变量值及控制范围
1)三种界定符:引号和反撇号
“ “ 双引号 界定范围
a=10 echo “$a” //正常调用变量
‘ ‘ 单引号 界定范围 屏蔽特殊符号的功能
echo ‘$a’ //无法调用变量,$被屏蔽
a=date //仅仅将四个字母赋值给a
a=date
//将date执行结果赋值给a
a=$(date) //效果同上2)read标准输入取值:read -p “提示信息” 变量名 -t 指定超时秒数
stty -echo 屏蔽回显#!/bin/bash read -p "请输入用户名" u //-p是可以定义提示信息,u相当于自 定义变量名称,可以存储用户看到提示信息后输入的字符 useradd $u read -p "请输入密码" n echo "$n" | passwd --stdin $u
stty echo 恢复回显
再次改良脚本:#!/bin/bash read -p "请输入用户名" u useradd $u stty -echo //隐藏键盘输入内容,设置密码时不显示 read -p "请输入密码" n stty echo echo "$n" | passwd --stdin $u
3)export声明全局变量
局部变量 仅仅在当前解释器进程中使用的变量
全局变量 解释器产生的子进程中也可以使用的变量a=10 //创建变量 export a //将已有变量a发布为全局效果 export b=20 //创建变量的同时发布为全局效果 export -n b //将全局变量恢复为局部变量 #注意,测试时要在父进程创建变量,然后可以执行bash进入子进程测试效果,如果要返回父进程要执行exit指令
shell运算
expr运算工具(运算,输出)
格式:expr 整数1 运算符 整数2
乘法操作应采用*转义,避免被作为Shell通配符$[ ] 运算(使用$(( )) 可以实现相同效果,只运算不输出)
格式:$[整数1 运算符 整数2……….]
乘号操作*无需转义,运算符两侧可以无空格
引用变量可省略$ 符号
计算结果替换表达式本身,可结合echo命令输出变量的自增/减等操作:$[ ]替换或者let命令完成
使用let命令,不输出结果,专用于变量的创建或者变量的自增减
变量的自增减:
常规写法 主流写法
let a=a+1 let a++ 变量a加1
let a=a-1 let a— 变量a减1
let a=a+10 let a+=10 变量a加10
let a=a-10 let a-=10 变量a减10
let a=a2 let a=2 变量a乘以2
let a=a/2 let a/=2 变量a除以2
let a=a%3 let a%=3 变量a除以3取余数小数运算:使用bc,可以进行小数计算
Bash内建机制仅支持整数值运算,expr命令、$[]算式替换不支持小数的运算
所以使用bc实现小数运算,支持高精度的数制运算,直接运行bc可进入交互式运算界面,quit退出,设置 scale=n 可约束小数位
]# echo “scale=2;10/3” | bc
3.33shell条件测试:可以赋予脚本智能判断的效果
语法格式:test 选项 参数 或 [ 表达式 ]1)字符串比较
-z 字符串的值为空
-n 字符串的值不为空,相当于!-z
== 两边是否相等,相等时条件测试算成功
!= 两边是否不等,不相等时条件测试算成功test a == a //方式一:test ,使用常量判断,再用echo $?测试结果 [ a == a ] //方式二:中括号,使用常量判断,再用echo $?测试结果 [ $a != $b ] //使用变量判断两个变量的值是否不相等 [ "$c" == $b ] //如果变量为空,有可能报错,加双引号可以避免
2)逻辑组合
条件 && 指令 条件成功才执行指令
条件 || 指令 条件失败才执行指令
A && B &&c A、B任务都成功算成功
A || B ||c A、B 任务有一个成功算成功[ root == $USER ] || exit //判断当前用户名是否为root,如果判断失败,则执行exit [ $USER != root ] && exit //判断当前用户名是否不为root,如果判断成功,则执行exit [ root != $USER ] && echo "非管理员不能执行该脚本" && exit //之前的语句可以改进成这个方式
3)数值比较
-eq 是否相等 -ne 是否不等 -gt 是否大于
-ge 是否大于等于 -lt 是否小于 -le 是否小于等于
编写脚本,每2分钟检查服务器的用户数量,如果发生变化则发邮件通知管理员
编写脚本前 echo “测试” | mail -s test root 测试下邮件功能是否正常yum -y install mailx postfix //如果不正常就安装软件包 systemctl start postfix //然后启动服务 #!/bin/bash x=$(cat /etc/passwd | wc -l) //将目前用户数量赋值给变量x [ $x -gt 29 ] && echo "用户数量发生变化,服务器可能被入侵" | mail -s test root //如果目前用户数量大于29,就发邮件给管理员,29是之前查看的用户数量 chmod u+x test01.sh //然后给脚本加x权限 crontab -e //编写计划任务 */2 * * * * /opt/test01.sh //定义每2分钟执行脚本
4)文件比较
-e 判断文件是否存在,不关心类型
-f 判断文件是否存在,必须是普通文件
-d 判断文件是否存在,必须是目录
-r 判断当前用户对文件是否有读权限,对root无效
-w 判断当前用户对文件是否有写权限,对root无效
-x 判断当前用户对文件是否有x权限,目录对root无效[ -d /etc/vsftpd ] && echo "yes"
if条件选择
单分支结构
if 条件测试;then //满足条件的话就执行下面指令
执行指令
fi#!/bin/bash if [ $UID -eq 0 ];then echo "我是管理员" echo ok fi
双分支结构
if 条件测试;then //满足条件的话就执行下面指令
执行指令A //指令可以是一条,也可以是多条
else //否则,不满足条件的话就执行下面指令
执行指令B
fi
使用if双分支编写ping脚本#!/bin/bash if [ $UID -eq 0 ];then echo "我是管理员" echo ok else echo "我不是管理员" echo no fi
#!/bin/bash ping -c 3 -i 0.2 -W 1 $1 &> /dev/null //使用$1位置变量 更方便 if [ $? -eq 0 ];then echo "通了!" else echo "不通!" fi
多分枝结构
if 条件测试;then //满足条件的话就执行下面A指令
执行指令A //指令可以是一条,也可以是多条
elif 条件测试;then //满足条件的话就执行下面B指令
执行指令B
elif 条件测试;then //满足条件的话就执行下面C指令
执行指令C
else //否则,不满足条件的话就执行下面指令
执行指令
fi#!/bin/bash read -p "月考分数:" x if [ $x -ge 90 ];then echo "优秀" elif [ $x -ge 80 ];then echo "良好" elif [ $x -ge 60 ];then echo "及格" else echo "不及格" fi
case分支结构:功能类似if,编写时语句比if精简
语法结构
case 调用的变量名 in
模式1) //检查变量的实际取值,与预设值匹配,则执行对应的操作
指令;;
模式2)
指令;;
*) //默认输出脚本用法
指令
esac脚本示例
#!/bin/bash case $1 in t) //如果$1是t就执行touch任务 touch $2;; m) //如果$1是m就执行mkdir任务 mkdir $2;; r) //如果$1是r就执行rm任务 rm -rf $2;; *) echo "请输入t或者m或者r" esac
使用case分支编写控制nginx服务的脚本
#!/bin/bash case $1 in start|kai) //输入start或者kai 就开启nginx /usr/local/nginx/sbin/nginx;; stop|STOP|s|guan) //输入stop或STOP或s或guan就关闭nginx /usr/local/nginx/sbin/nginx -s stop;; re|cq) //输入re或者cq就重启nginx /usr/local/nginx/sbin/nginx -s stop /usr/local/nginx/sbin/nginx;; cx|status) //输入cx或者status就查询nginx状态 netstat -ntulp | grep -q nginx s //查询nginx,但不输出结果 -q选项是保持沉默 [ $? -eq 0 ] && echo "nginx正在运行" || echo "nginx未开启";; //判断,如果上述任务成功,说明nginx服务开启,就显示"nginx正在运行",如果上述任务失败,说名nginx服务关闭,就显示"nginx未开启" *) echo "请输入start或stop" esac
shell循环结构
for循环:可以定义循环次数
for 变量名称 in 值1 值2 值3 。。。。
do
任务
done
//此处变量名可以自定义,通常用i,值的多少决定了下面do与done之间的任务执行多少次,每个值之间有空格,这里是有3个值,所以就循环执行指令3次C语言风格的for循环
for ((初值;条件;步长))
do
任务
done编写脚本,可以测试192.168.2.1~192.168.2.10
#!/bin/bash for i in {1..10} do ping -c 3 -i 0.2 -W 1 192.168.2.$i &> /dev/null if [ $? -eq 0 ];then echo "192.168.2.$i 通了" else echo "192.168.2.$i 不通" fi done
改良版,可以统计最终通了和不通的数量
#!/bin/bash x=0 y=0 for i in {1..10} do ping -c 3 -i 0.2 -W 1 192.168.2.$i &> /dev/null if [ $? -eq 0 ];then echo "192.168.2.$i 通了" let x++ //如果通了一台就把变量x+1 else echo "192.168.2.$i 不通" let y++ //如果不通了一台就把变量y+1 fi done echo "$x台通了,$y台不通"
while循环:条件式循环
while 条件测试
do
任务
done
//根据条件的结果决定是否要执行任务,条件测试成功的话就执行,如果失败立刻结束循环脚本示例
```shell!/bin/bash
while : #冒号代表永远正确 do echo abc sleep 0.1 //休息0.1秒 done
!/bin/bash
n=10 while [ $n -ge 5 ] #可以根据条件决定是否要循环 do echo abc sleep 0.1 let n— //每次循环将n-1 done
<a name="TPC4x"></a>
#### 循环的控制
**exit** 可以终止循环,但脚本也终止<br />**break** 可以终止循环,继续循环后的任务<br />**continue** 可以终止当前循环,继续下一次循环<br />编写脚本,帮用户进行整数求和,如果用户输入0就结束求和,并输出结果
```shell
#!/bin/bash
x=0
while : //while循环后面写冒号代表永远正确可以无限循环
do
read -p "请输入一个整数求和(0是结束并输出结果):" n
[ -z $n ] && continue //如果n是空值则重新进行循环任务
[ $n -eq 0 ] && break //如果n是0则退出循环执行循环后任务
let x+=n //不断的将n的值保存在x里
done
echo "总和是$x"
shell函数:
可以利用一个名称存储公共的语句块,实现精简脚本方便后期调用的目的
使用函数的好处:使脚本代码更简洁,增强易读性;提高Shell脚本的执行效率
适用于比较复杂的启动/终止控制操作,方便在需要时多次调用
echo输出不同的颜色
echo -e “\033[34mABCD\033[0m” //使用echo输出不同颜色的内容
#!/bin/bash //定义不同颜色的函数
a(){
echo -e "\033[$1m$2\033[0m"
}
a 31/91 red //各种颜色的色块
a 32/92 green
a 33/93 yellow
a 34/94 blue
a 35/95 purple
函数语法格式
abc(){ //创建函数
指令
}
abc //调用函数
或
function 函数名 {
指令
}
利用函数完善之前控制nginx服务,使输出信息时有不同颜色
#!/bin/bash
a(){
echo -e "\033[$1m$2\033[0m"
}
case $1 in
.........
netstat -ntulp | grep -q nginx
[ $? -eq 0 ] && a 32 "nginx正在运行" || a 31 "nginx未开启";; //输出不同的颜色
*)
echo "请输入start或stop"
esac
文本的处理
字符串的处理
1)字符串的截取
${变量名称:截取位置:截取长度}
[root@proxy opt]# a=abcd
[root@proxy opt]# echo ${a:1:2} //从第二位截取两位
bc
[root@proxy opt]# echo ${a:0:2} //从头截取两位,截取位置的0可以不写
ab
编写脚本,可以生成8位随机字符用作密码
#!/bin/bash
x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
for i in {1..8}
do
n=$[RANDOM%62] //得到0~61随机数存在变量n中
a=${x:n:1} //截取到1个随机字符,存在变量a中
c=$a$c //不断往c中追加,此时c相当于一个袋子
done
echo $c //最后喊出,得到8位长度随机字符串
2)字符串替换
${变量名/旧/新} //只替换第1个匹配结果
${变量名//旧/新} //替换全部匹配结果
示例
a=112233
echo ${a/2/6} //将1个字符2换成6
echo ${a//2/6} //将所有字符2换成6
a=1122233
echo ${a/11/} //将11替换成空,相当于删除
3)字符串的删除,可以删除两边
${变量名#要删除的内容} 掐头,从左往右最短匹配
${变量名##要删除的内容} 掐头,从左往右最长匹配
${变量名%要删除的内容} 去尾,从右往左最短匹配
${变量名%%要删除的内容} 去尾,从右往左最长匹配
示例
a=abcdef //创建变量,作为素材
echo ${a#abc} //掐头,删除到abc
echo ${a%def} //去尾,删除到def
echo ${a#abcde} //掐头,删除到abcde
a=abcdefghijk //创建变量,作为素材
echo ${a#abcdefghi} //掐头,删除到i
echo ${a#*i} //效果同上,精简写法
echo ${a%defghijk} //去尾,删除到d
echo ${a%d*} //效果同上,精简写法
脚本应用:可以批量修改扩展名
#!/bin/bash
for i in $(ls *.txt) //找到所有的txt文件交给for循环
do
n=${i%.*} //用去尾的方法删除扩展名
mv $i $n.doc //再将源文件扩展名修改为doc
done
4)定义变量初值(备用)
${变量名:-初值}
若变量值非空则返回$变量的值,否则返回字符串“初值”
脚本应用:改密码脚本
#!/bin/bash
read -p "请输入用户名:" u
[ -z $u ] && echo "必须输入用户名!" && exit
useradd $uread -p "请输入密码(默认123456):" n
echo ${n:-123456} | passwd --stdin $u //n值为空时,启用默认密码123456
正则表达式
基本元字符
行首尾及单字匹配
grep ^root user //找以root开头的行
grep bash$ user //找以bash结尾的行
grep ^$ user //找空行
grep -v ^$ user //显示除了空行的内容
grep "." user //找任意单个字符,文档中每个字符都可以理解为任意字符
grep "r..t" user //找rt之间有2个任意字符的行
grep "r.t" user //找rt之间有1个任意字符的行,没有匹配内容,就无输出
未定匹配次数
grep "ro\{1,\}t" user //使用基本正则找o出现1次以及1次以上
egrep "ro{1,}t" user //使用扩展正则,效果同上,比较精简
egrep "ro+t" user //使用扩展正则,效果同上,最精简
grep "." user //找任意单个字符,文档中每个字符都可以理解为任意字符
grep "r..t" user //找rt之间有2个任意字符的行
grep "r.t" user //找rt之间有1个任意字符的行,没有匹配内容,就无输出
grep "*" user //错误用法,*号是匹配前一个字符任意次,不能单独使用
grep "ro*t" user //找rt,中间的o有没有都行,有几次都行
grep ".*" user //找任意,包括空行 .与*的组合在正则中相当于通配符的效果
{ }限定次数
grep "ro\{1,2\}t" user //找rt,中间的o可以有1~2个
grep "ro\{2,6\}t" user //找rt,中间的o可以有2~6个
grep "ro\{1,\}t" user //找rt,中间的o可以有1个以及1个以上
grep "ro\{3\}t" user //找rt,中间的o必须只有有3个
其他元字符
[ ]范围内单字匹配
[:space:]:空白字符
[:punct:]:标点符号
[:lower:]:小写字母
[:upper:]:大写字母
[:alpha:]:大小写字母
[:digit:]:数字
[:alnum:]:数字和大小写字母
grep "[root]" user //找r、o、t任意一个字符
grep "[rot]" user //效果同上
grep "[^rot]" user //显示r或o或t以外的内容
grep "[0123456789]" user //找所有数字
grep "[0-9]" user //效果同上
grep "[^0-9]" user //显示数字以外内容
grep "[a-z]" user //找所有小写字母
grep "[A-Z]" user //找所有大写字母
grep "[a-Z]" user //找所有字母
grep "[^0-9a-Z]" user //找所有符号
整体及边界匹配
egrep "(0:){2}" user //找连续的2个0: 小括号的作用是将字符组合为一个整体
egrep "root|bin" user //找有root或者bin的行
egrep "the\b" abc.txt //在abc.txt文件中找the,右边不允许出现数字、字母、下划线
egrep "\bthe\b" abc.txt //两边都不允许出现数字、字母、下划线
egrep "\<the\>" abc.txt //效果同上
egrep "the\w" abc.txt //在abc.txt文件中找the*,*为数字字母下划线
sort排序文件并输出
- sort命令将文件进行排序,并将排序结果标准输出。sort命令既可以从特定的文件,也可以从stdin中获取输入
- 语法格式:sort [参数] [文件] | -b | 忽略每行前面开始出的空格字符 | | —- | —- | | -c | 检查文件是否已经按照顺序排序 | | -d | 排序时,处理英文字母、数字及空格字符外,忽略其他的字符 | | -f | 排序时,将小写字母视为大写字母 | | -i | 排序时,除了040至176之间的ASCII字符外,忽略其他的字符 | | -m | 将几个排序号的文件进行合并 | | -M | 将前面3个字母依照月份的缩写进行排序 | | -n | 依照数值的大小排序 | | -o <输出文件> | 将排序后的结果存入制定的文件 | | -r | 以相反的顺序来排序 | | -t <分隔字符> | 指定排序时所用的栏位分隔字符 | | -k | 指定需要排序的栏位 |
参考实例
sort将文件/文本的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出
[root@linuxcool ~]# cat sort.txt
AA:10:1.1
CC:30:3.3
DD:40:4.4
BB:20:2.2
FF:60:6.6
FF:60:6.6
EE:50:5.5
[root@linuxcool ~]# sort sort.txt
AA:10:1.1
BB:20:2.2
CC:30:3.3
DD:40:4.4
EE:50:5.5
FF:60:6.6
FF:60:6.6
忽略相同行使用-u选项
[root@linuxcool ~]# cat sort.txt
AA:10:1.1
CC:30:3.3
DD:40:4.4
BB:20:2.2
FF:60:6.6
FF:60:6.6
EE:50:5.5
[root@linuxcool ~]# sort -u sort.txt
AA:10:1.1
BB:20:2.2
CC:30:3.3
DD:40:4.4
EE:50:5.5
FF:60:6.6
sort的-n、-r、-k、-t选项的使用
[root@linuxcool ~]# cat sort.txt
AA:BB:CC
aa:30:1.9
cc:50:3.3
dd:20:4.2
bb:10:2.4
ee:40:5.3
ee:60:5.1
[root@linuxcool ~]# sort -nk 2 -t: sort.txt
AA:BB:CC
bb:10:2.4
dd:20:4.2
aa:30:1.9
ee:40:5.3
cc:50:3.3
ee:60:5.1
[root@linuxcool ~]# sort -nrk 3 -t: sort.txt
ee:40:5.3
ee:60:5.1
dd:20:4.2
cc:50:3.3
bb:10:2.4
aa:30:1.9
AA:BB:CC
sed (Stream EDitor) 流式编辑器
可以对文档进行非交互式增删改查,逐行处理
非交互,基于模式匹配过滤及修改文本
逐行处理,并将结果输出到屏幕
sed用法
1,前置指令 | sed 选项 条件 指令
2,sed 选项 条件 指令 被处理文档
选项 :-n 屏蔽默认输出 -r 支持扩展正则 -i 修改源文件
条件 :行号 /字符串/
指令 :p 输出 d 删除 s 替换 a 行下追加 i 行上添加 c 替换整行
sed p(输出)/d(删除)命令
sed -n 'p' user //输出所有行
sed -n '3p' user //输出第3行
sed -n '2,4p' user //输出2~4行
sed -n '2p;4p' user //输出第2行与第4行
sed -n '3,+1p' user //输出第3行以及后面1行
sed -n '1~2p' user //输出奇数行1为起始行2为步长
sed -n '/^root/p' user //输出以root开头的行
sed -n '/root/p' user //输出包含root的行
sed -nr '/^root|^bin/p' user //输出以root开头的行或bin开头的行,|是扩展正则,需要r选项
sed -n '1!p' user //输出除了第1行的内容,!是取反
sed -n '$p' user //输出最后一行
sed -n '=' user //输出行号,如果是$=就是最后一行的行号
#以上操作,如果去掉-n,再将p指令改成d指令就是删除
sed '3,5d' abc.txt #删除第3~5行
sed '/xml/d' abc.txt #删除所有包含xml的行
sed '/xml/!d' abc.txt #删除不包含xml的行
sed '/^install/d' abc.txt #删除以install开头的行
sed '$d' abc.txt #删除文件的最后一行
sed '/^$/d' abc.txt #删除所有空行
sed s(替换)命令
sed's/2017/6666/'shu.txt //把所有行的第1个2017替换成6666
sed's/2017/6666/2'shu.txt //把所有行的第2个2017替换成6666
sed'1s/2017/6666/'shu.txt //把第1行的第1个2017替换成6666
sed'3s/2017/6666/3'shu.txt //把第3行的第3个2017替换成6666
sed's/2017/6666/g'shu.txt //所有行的所有个2017都替换
sed'/2024/s/2017/6666/g'shu.txt //找含有2024的行,将所有2017替换成6666
sed '4,7s/^/#/' a.txt //将4~7行注释掉
#如果想把 /bin/bash 替换成 /sbin/sh 怎么操作?
sed 's/\/bin\/bash/\/sbin\/sh/' user //使用转义符号可以成功,但不方便
sed 's!/bin/bash!/sbin/sh!' user //最佳方案,更改s的替换符
sed -i '/SELINUX/s/enforcing/disabled/' /etc/selinux/config # 修改selinxu的配置文件模式
脚本练习
搭建httpd服务,用82号端口开启服务
#!/bin/bash
setenforce 0 //关闭selinux
yum -y install httpd &> /dev/null //安装网站
echo "sed-test~~~" > /var/www/html/index.html //定义默认页
sed -i '/^Listen 80/s/0/2/' /etc/httpd/conf/httpd.conf //修改配置文件,将监听端口修改为82
systemctl restart httpd //开服务
systemctl enable httpd //设置开机自启
然后运行脚本
curl 192.168.2.5:82 //脚本运行之后,测试82端口看到页面即可
sed-test~~~
netstat -ntulp | grep httpd //检查服务的端口是否为82
找到系统使用bash的账户名,然后按照“用户名 —> 密码” 的格式存储在一个文件中
#!/bin/bash
u=$(sed -n '/bash$/s/:.*//p' /etc/passwd) //找到passwd文档中以bash结尾的行,然后将行中冒号以及冒号后面内容都删除此处的p代表仅仅显示s替换成功的行,最后赋值给u
for i in $u //将那些用bash的账户名交给for循环
do
n=$(grep $i: /etc/shadow) //用每个账户名去shadow中找对应信息
n=${n#*:} //掐头,从左往右删除到第1个冒号cat
n=${n%%:*} //去尾,从右往左删除到最后一个冒号
#经过上述步骤,n就是最终要的密码了
echo "$i --> $n" //按格式喊出,如果要存到文件中就用追加重定向
done
sed其他指令(a行下追加 i行上添加 c替换整行)
sed 'a 666' user //所有行的下面追加666
sed '1a 666' user //第1行的下面追加666
sed '/^bin/a 666' user //在以bin开头的行的下面追加666
sed 'i 666' user //所有行的上面添加666
sed '5i 666' user //第5行的上面添加666
sed '$i 666' user //最后1行的上面添加666
sed 'c 666' user //所有行都替换成666
sed '1c 666' user //替换第1行为666
awk指令:可以实现精确搜索并输出 ,逐行处理
awk用法
1,前置指令 | awk 选项 条件 指令
2,awk 选项 条件 指令 被处理文档
选项 -F 定义分隔符
指令 print
条件 /字符串/
内置变量 $1第一列 $2第二列 $3第三列 … $0 所有列 NR行号 NF 列号
awk '{print}' abc.txt //输出所有
awk '/to/{print}' abc.txt //输出有to的那行
awk内置变量
内置变量 $1第一列 $2第二列 $3第三列 … $0 所有列 NR行号 NF 列号
awk '{print $2}' abc.txt //输出所有行的第2列
awk '/to/{print $1}' abc.txt //输出有to的那行的第1列
awk '{print $0}' abc.txt //输出所有行所有列
awk '{print $0,$1}' abc.txt //输出所有行所有列,第1列
awk '{print NR}' abc.txt //输出所有行的行号
awk '{print NR,$0}' abc.txt //输出所有行的行号,所有列
awk '{print NR,NF}' abc.txt //输出所有行的行号,列号(有几列)
awk -F: '{print $1}' user //文档中如果没有空格,可以用F修改分隔符
awk -F: '{print $1,$6}' user //使用冒号作为列的分隔符,显示第1、6列
awk -F: '{print $1" 的家目录是 "$6}' user //还可以输出常量,加双引号即可
关于awk内置变量的书签:
收集根分区剩余容量
df -h | awk '/\/$/{print $4}' //使用df -h 作为前置指令交给awk处理找到以/结尾的行,并输出第4列
df -h | awk '/\/$/{print "根分区剩余容量是"$4}' //然后加常量输出
收集网卡流量信息
ifconfig eth0 | awk '/RX p/{print "eth0网卡接收的数量量是"$5}'
ifconfig eth0 | awk '/TX p/{print "eth0网卡发送的数量量是"$5}'
awk的条件
/字符串/ : 还可以使用正则 ~ 包含 !~不包含
awk -F: '$6~/bin/{print}' user //输出第6列包含bin的行
awk -F: '$6!~/bin/{print}' user //输出第6列不包含bin的行
使用数字或者字符串:== != > >= < <=
awk -F: '$3<3{print}' user //输出第3列小于3的行
awk -F: '$3<=3{print}' user //输出第3列小于等于3的行
awk -F: 'NR==2{print}' user //输出第2行
awk -F: 'NR>2{print}' user //输出行号大于2的行
逻辑组合:&&并且 ||或者
awk -F: 'NR==2||NR==4{print}' user //找行号是2或者4的行
awk -F: 'NR==2||NR==40{print}' user //如果只有一个条件满足就显示一个
awk -F: '$7~/bash/&&$3<=500{print}' user //找第7列包含bash并且第3列小于等于500的行
awk 'NR==2&&NR==4{print}' user //找行号既是2又是4的行,不存在,无输出
awk -F: '$7~/bash/&&NR<=3{print}' user //找第7列包含bash并且行号是1~3的
awk -F: '$7~/bash/||NR<=3{print}' user //找第7列包含bash或者行号是1~3的
awk -F: '$1~/root/' user //找第1列包含root的行
如果有用户叫root6,也会搜到,比较宽松的搜索方式,如果任务就是{print}的话可以省略不写
awk -F: '$1=="root"' user //找第1列完全等于root的行多一个字符少一个字符都不行,比较严格的搜索方式
运算
awk 'NR%2==0{print NR,$0}' user //在条件中使用运算,找到将行号除以2余数等于0的行,然后输出该行的行号和所有列,相当于输出偶数行
awk处理时机:可以执行额外任务
BEGIN任务 执行1次,读取文档之前执行
逐行任务 执行n次,读取文档时执行
END任务 执行1次,读取文档之后执行
awk -F: 'BEGIN{print "ok"}{print $1}END{print "ok"}' user
awk 'BEGIN{print NR}{print NR}END{print NR}' user
awk求和、求平均、求最大值、求最小值
cat data|awk '{sum+=$1} END {print "Sum = ", sum}'
cat data|awk '{sum+=$1} END {print "Average = ", sum/NR}'
cat data|awk 'BEGIN {max = 0} {if ($1>max) max=$1 } END {print "Max=", max}'
#min的初始值设置一个超大数即可)
awk 'BEGIN {min = 1999999} {if ($1<min) min=$1} END {print "Min=", min}'
利用awk处理时机,输出下列内容
awk 'BEGIN{print "User\tUID\tHome"}' //第1步输出表头信息 \t制表符
awk -F: '{print $1"\t"$3"\t"$6}' user //第2步输出内容
awk 'END{print "总计"NR"行" }' user //第3步输出结尾
awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1"\t"$3"\t"$6}END{print "总计"NR"行"}' user //合在一起写
awk数组+for循环实现高级搜索
数组 相当于可以存储多个值的特殊变量
数组名[下标]=下标对应的值
awk数组
awk 'BEGIN{a[1]=10;a[2]=20;print a[2],a[1]}' //使用awk测试数组,首先创建数组a,下标1对应值是10,下标2对应值是20,然后输出下标是2与下标是1的值
awk 'BEGIN{a["abc"]="abcabc";a["xyz"]="xyzxyz";print a["xyz"]}' //数组的下标和值都可以不是数字,测试时加双引号即可
示例
准备一个文档,里面有6行,每行分别是abc、xyz、abc、opq、xyz、abc 然后按照awk逐行处理的工作特点使用awk ‘{a[$1]++}’ shu.txt 走完每一行得到下列结果
但不会输出到屏幕
a[$1]++ a[abc]++ a[abc]=1
a[$1]++ a[xyz]++ a[xyz]=1
a[$1]++ a[abc]++ a[abc]=2
a[$1]++ a[opq]++ a[opq]=1
a[$1]++ a[xyz]++ a[xyz]=2
a[$1]++ a[abc]++ a[abc]=3
如果要输出到屏幕可以使用命令
awk ‘{a[$1]++}END{print a[“abc”]}’ shu.txt
awk+for循环
for(变量名 in 数组名){print 变量名}//这个格式可以查看数组的所有下标
awk '{a[$1]++}END{for(i in a){print i,a[i]}}' shu.txt //使用逐行任务与数组收集文档shu.txt中的信息,然后在END任务中使用for循环显示所有数组a的下标与值
awk实际脚本应用
使用awk统计网站访问量
setenforce 0 //关闭selinux
systemctl stop firewalld //关闭防火墙
systemctl restart httpd //开启网站服务
初步统计
curl 192.168.4.7:82 //如果端口没改过就不用敲
awk '{print $1}' /var/log/httpd/access_log //初步统计,不完美
进阶统计
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}'
#/var/log/httpd/access_log //将上述的文件替换成网站的日志,就可以最终用来查看日志得到可以得到哪个ip来访以及来访的次数
最终统计
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
//使用sort命令增加排序功能,-n是以数字形式排序,-r是降序, -k是指定为第几列排序
编写安全监测脚本
要求监测如下数据项目:cpu负载;网卡流量;内存剩余容量;磁盘剩余容量;计算机账户数量;当前登陆账户数量;计算机当前开启的进程数量;本机已安装的软件包数量
#!/bin/bash
while :
do
clear
free -h | awk '/^Mem:/{print "剩余内存容量是"$4}'
df -h | awk '/\/$/{print "根分区剩余容量是"$4}'
awk 'END{print "用户总数是"NR"个"}' /etc/passwd
who | awk 'END{print "登录用户数量是"NR"个"}'
uptime | awk '{print "cpu的15分钟平均负载是"$NF}'
rpm -qa | awk 'END{print "安装的软件包数量是"NR"个"}'
sleep 3
done
防止ssh暴力破解密码
/var/log/secure是安全日志,如果有人登陆时输入错误密码的话信息会记录下来,这种信息可以用awk抓取出来
awk '/Failed password for root/{ip[$11]++}END{for(i in ip){print i,ip[i]}}' /var/log/secure
#统计安全日志中访问root账户密码输入错误的ip地址与次数