shell编程从入门到高级教程
一.shell编程初识
1.1 shell能做什么
1. 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...)
2. 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)
3. 应用管理程序 (KVM,集群管理扩容,MySQL,DELLR720批量RAID)
4. 日志分析处理程序(PV, UV, 200, !200, top 100, grep/awk)
5. 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
6. 自动化管理程序(批量远程修改密码,软件升级,配置更新)
7. 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
8. 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
9. 自动化扩容(增加云主机——>业务上线)
zabbix监控CPU 80%+|-50% Python API AWS/EC2(增加/删除云主机) + Shell Script(业务上线)
10. 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序算法实现,等众多功能的一个实现
11. Shell可以做任何事(一切取决于业务需求)
1.2 shell编程初识
shell的定义
Shell 是命令解释器
Shell 也是一种程序设计语言,它有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语言可以编写功能很强、代码简短的程序**
shell的分类和更改
cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
默认shell: bash
注意:各种脚本里凡是/bin/sh表示的是使用默认bash
查看当前使用的shell:
#echo $SEHLL
shell 的更改:
#chsh
#vim /etc/passwd
适用范围
什么时候不适合使用Shell编程:
1. 资源紧张的项目,特别是那些速度是重要因素的地方(排序,散序,等等)
2. 程序要进行很复杂的数学计算,特别是浮点计算,任意精度的计算,或者是复数计算
3. 要求交叉编译平台的可移植性(使用C或者是Java代替)
4. 需要结构化编程的复杂应用(需要变量类型检查和函数原型等等)
5. 对于影响系统全局性的关键任务应用。
6. 安全非常重要。你必须保证系统完整性和抵抗入侵,攻击和恶意破坏。
7. 项目由连串的依赖的各个部分组成。
8. 多种文件操作要求(Bash被限制成文件顺序存取,并且是以相当笨拙,效率低下的逐行的存取方式)
9. 需要良好的多维数组支持。
10. 需要类似链表或树这样的数据结构。
11. 需要产生或操作图象或图形用户界面。
12. 需要直接存取系统硬件。
13. 需要端口号或是socket I/O。
14. 需要使用可重用的函数库或接口。
15. 所有的私有的不开源的应用程序(Shell脚本的源代码是直接可读,能被所有人看到的)
如果你需要有上面的任意一种应用,请考虑其他的更强大的脚本语言――Perl,Tcl,Python,Ruby,或者可能是其他更高级的编译型语言,例如C,C++或者是Java
实例(仅展示用,后续有详细讲解)
相对于shell脚本是可以实现大多数自动化部署功能,可以快速的提升工作效率,比如如下脚本,实现众多 批量化部署
[root@master1 ~]# cat jiaoben.sh
#!/usr/bash
liu () {
cat <<-EOF
1.关闭防火墙,一键三连,修改selinux
2.查看系统状态[磁盘大小和使用率 内存大小和使用率 cpu负载 运行时间 版本号]
3.一键配置yum源,一键三联,关闭防火墙,
4.修改主机名,配置静态IP
5.yum安装mysql-5.7
6.安装yum安装 nginx
7.安装jdk8.tomcat8版本
EOF
}
one (){
systemctl stop firewalld
iptables -X && iptables -F && iptables -Z
systemctl disable firewalld && systemctl status firewalld
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config && getenforce
}
two () {
cat <<-EOF
1.磁盘大小和使用率
2.内存大小和使用率
3.cpu负载
4.运行时间
5.版本号
}
EOF
printf "\e[1;31m 请选择你想使用的功能(1/2/3/4/5): \e[0m" && read ww
case $ww in
1)
a=`df -Th |awk '{print $1,$2,$5}'` ##查询所有磁盘内存信息
echo "磁盘大小是使用率分别为: $a"
;;
2)
b=`free -h |grep 'Mem' |awk '{print $2,$3}'`
echo "内存大小和使用率分别为: $b"
;;
3)
c=`uptime |awk '{print $11,$12,$13}'`
echo "cpu 负载为: $c"
;;
4)
d=`uptime |awk '{print $3,$4}'`
echo "运行时间为: $d"
;;
5)
e=`uname -r`
echo "版本号为: $e"
;;
*)
echo "error select"
;;
esac
}
tree () {
f=`cat /etc/redhat-release |awk '{print $4}' |awk -F'.' '{print $1}'`
if [ $f -eq 7 ];then
echo "此版本为centos $f 的版本"
read -p "是否重新配置yum源 (y|n): " yn
if [ "$yn" = "y" ];then
yum install -y wget ntpdate net-tools vim bash-completion ShellCheck
systemctl stop firewalld
iptable -X && iptable -F && iptable -Z
systemctl disable firewalld
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
setenforce 0
cp /etc/yum.repos.d/* /tmp/ && rm -rf /etc/yum.repos.d/*
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum clean all && yum makecache fast
ntpdate -b ntp1.aliyun.com
else
echo "不需要重新配置yum源"
fi
fi
}
four () {
cat <<-EOF
"请输入您的选项"
1.修改主机名
2.配置静态ip
EOF
printf "\e[1;31m 请选择你想使用的功能(1/2): \e[0m" && read wen
case $wen in
1)
read -p "请输入你的主机名" name
hostnamectl set-hostname $name &&bash
;;
2)
cp /etc/sysconfig/network-scripts/ifcfg-ens33 /tmp/ifcfg-ens33.bak
read -p "请输入你的ip的末尾" ipname
echo "TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.229.$ipname
PREFIX=24
GATEWAY=192.168.229.2
DNS1=223.5.5.5
DNS2=223.6.6.6" >/etc/sysconfig/network-scripts/ifcfg-ens33
systemctl restart network
;;
*)
echo "eroor select"
;;
esac
}
five () {
if rpm -qa |grep "mysql" |egrep -v grep > /dev/null
then
echo cunzai!
else
echo no !
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
systemctl stop firewalld && systemctl disable firewalld
yum -y groupinstall "Development Tools"
cd /usr/local/src/
wget https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
rpm -ivh mysql80-community-release-el7-1.noarch.rpm
sed -ri 21s/enabled=0/enabled=1/g /etc/yum.repos.d/mysql-community.repo
sed -ri 28s/enabled=1/enabled=0/g /etc/yum.repos.d/mysql-community.repo
yum -y install mysql-community-server
systemctl start mysqld && systemctl enable mysqld
grep "password" /var/log/mysqld.log
fi
}
six (){
if rpm -qa |grep "nginx" |egrep -v grep > /dev/null
then
echo cunzai!
else
echo no !
yum -y install gcc gcc-c++ pcre pcre-devel
yum -y install nginx
fi
}
liu
printf "\e[1;31m 请选择你想要实现的功能(1/2/3/4/5/6): \e[0m" && read sb
case $sb in
1)one
;;
2)two
;;
3)tree
;;
4)four
;;
5)five
;;
6)six
;;
*)
echo "eroor select"
esac
1.3 shell编程特点
bash特性
补全
历史
别名
快捷键
前后台作业
重定向
管道
命令排序执行
; && ||
通配符
{} ? *
正则表达式
脚本
历史命令
查看历史命令
history /etc/profile 下的historysize 可以修改
调用历史命令
上下健
!关键字
!历史命令行号
!! 执行上一条命令
!$ 上一条命令
alt+.
esc . 上一条命令的最后一个参数
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
关键字+pgup/phdn 可以切换关键字相关的历史命令
显示历史命令执行时间:
1.设置变量:
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
2.再次执行history查看结果
别名
查看别名
alias
设置别名
临时设置
Bash部分快捷键(常用) ,这里采用的都是Linux编辑器上的常用快捷键,有利于高效的展开工作
Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)
Ctrl+e 切换到命令行末尾
Ctrl+u 清除剪切光标之前的内容
Ctrl+k 清除剪切光标之后的内容
ctrl+y 粘贴刚才锁删除的字符
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
通配符置换
*,?,[],{}
例:
字符 含义 实例
* 匹配 0 或多个字符 a*b a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
? 匹配任意一个字符 a?b a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b。
[list] 匹配 list 中的任意单一字符 a[xyz]b a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb。
[!list] 匹配 除list 中的任意单一字符 a[!0-9]b a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[^list] 匹配 除list 中的任意单一字符 a[^0-9]b a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b 0与9之间必须也只能有一个字符 如a0b, a1b... a9b。
{string1,string2,...} 匹配 sring1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b a与b之间只能是abc或xyz或123这三个字符串之一。
1.4shell脚本规范
[root@master1 ~]# vim helloworld.sh
---.sh代表这个文件是个shell脚本,拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本
1. #!/usr/bin/env bash ---shebang蛇棒, 解释器, 翻译
2. #
3. # Author: tiger
3. # Email: [email protected]
4. # Github: https://github.com/tigerfive ---这就是注释, 你没看错
5. # Date: 2019/**/**
6.
7. printf "hello world\n"
[root@master1 ~]# sh helloworld.sh #sh命令是启动脚本
hello world
[root@master1 ~]# ./helloworld.sh #./ 也是启动sh 脚本命令
第一行: “#!/usr/bin/env bash”叫做shebang, shell语法规定shell脚本文件第一行为整个文件的解释器
第二行: 为“#”开头的行为注释行默认不会被程序所读取, 用来说明文件及标定所属人员使用, 也可用来解释程序
第七行: 为格式化打印语句printf, printf可以把后面的“hello world”打印到指定的终端中, \n为换行符
1.5 脚本运行方式
创建bash脚本
1.创建脚本文件
指定命令解释器
注释
编写bash指令集合
2.修改权限
bash脚本的执行
bash
#./scripts
#/shelldoc/scripts
#. ./scripts 使用当前shell执行
#source ./scripts 使用当前shell执行 比如cd /tmp会改变当前shell环境,但是其他的方式不会
#bash scripts
子shell
(cmds) 表示开启子shell
# pwd
/root/shell
# (cd /tmp;touch test;)
# ls /tmp/test
/tmp/test
# pwd
/root/shell
子shell能够继承父shell的一些属性,但是子shell不能够反过来改变父shell的属性
子shell的好处之一可以将复杂的任务分成多个小任务,并行处理,加快执行速度。
所有shell的父shell父进程:init
{cmds} 不开启子shell
# { cd /tmp;touch test1; }
# pwd
/tmp
bash脚本测试
•sh –x script
这将执行该脚本并显示所有变量的值
•sh –n script
不执行脚本只是检查语法模式,将返回所有错误语法
•sh –v script
执行脚本前把脚本内容显示在屏幕上
1.6变量的类型 【要点】
变量
shell 变量? 用一个固定的字符串去表示不固定的内容
bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能
变量分类
预定义变量、环境变量、自定义变量、位置变量
变量的类型:
1. 自定义变量
定义变量: 变量名=变量值 变量名必须以字母或下划线开头,区分大小写 ip1=192.168.2.115
引用变量: $变量名 或 ${变量名}
查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)
取消变量: unset 变量名
作用范围: 仅在当前shell中有效
[root@master ~]# a=8
[root@master ~]# echo $a
8
[root@master ~]# echo $a2
[root@master ~]# echo ${a}2
82
环境变量:shell在开始执行时已经定义好的
env 查看所有环境变量
set 查看所有变量
环境变量拥有可继承性:export之后就拥有继承性
export 导出变量(作用范围)
临时生效
永久生效
写道4个登陆脚本中 ~/.bashrc ~/profile
更好放在/etc/profile.d/* 下建立独立的环境变量配置文件
jdk
常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH
PATH:存储所有命令所在的路径
3. 位置变量
$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
4. 预定义变量
$0 脚本名
$* 所有的参数
$@ 所有的参数
$# 参数的个数
$$ 当前进程的PID
$! 上一个后台进程的PID
$? 上一个命令的返回值 0表示成功
示例1:
# vim test.sh
echo "第2个位置参数是$2"
echo "第1个位置参数是$1"
echo "第4个位置参数是$4"
echo "所有参数是: $*"
echo "所有参数是: $@"
echo "参数的个数是: $#"
echo "当前进程的PID是: $$"
echo '$1='$1
echo '$2='$2
echo '$3='$3
echo '$*='$*
echo '$@='$@
echo '$#='$#
echo '$$='$$
了解$*和$@区别
变量赋值
变量的赋值方式:
1. 显式赋值
变量名=变量值
示例:
ip1=192.168.229.61
school="BeiJing 1000phone"
today1=`date +%F`
today2=$(date +%F)
2. read 从键盘读入变量值
read 变量名
read -p "提示信息: " 变量名
read -t 5 -p "提示信息: " 变量名
read -n 2 变量名
示例一:ping,采用变量写一个,判断ip地址能否ping通
[root@master ~]# cat ping2.sh
#!/bin/bash
read -p "Input IP: " ip
ping -c2 $ip &>/dev/null
if [ $? = 0 ];then
echo "host $ip is ok"
else
echo "host $ip is fail"
fi
[root@master ~]# sh ping2.sh
Input IP: 192.168.229.61 #输入您需要ping的IP地址
host 192.168.229.61 is ok #显示ok则以完成测试
定义或引用变量时注意事项:
" " 弱引用
' ' 强引用
[root@master ~]# school=1000phone
[root@master ~]# echo "${school} is good"
1000phone is good
[root@master ~]# echo '${school} is good'
${school} is good
` ` 命令替换 等价于 $() 反引号中的shell命令会被先执行
[root@master ~]# touch `date +%F`_file1.txt
[root@master ~]# touch $(date +%F)_file2.txt
[root@master ~]# disk_free3="df -Ph |grep '/$' |awk '{print $4}'" 错误
[root@master ~]# disk_free4=$(df -Ph |grep '/$' |awk '{print $4}')
[root@master ~]# disk_free5=`df -Ph |grep '/$' |awk '{print $4}'`
变量运算
1. 整数运算
方法一:expr
expr 1 + 2
expr $num1 + $num2 + - \* / %
方法二:$(())
echo $(($num1+$num2)) + - * / %
echo $((num1+num2))
echo $((5-3*2))
echo $(((5-3)*2))
echo $((2**3))
sum=$((1+2)); echo $sum
方法三:$[]
echo $[5+2] + - * / %
echo $[5**2]
方法四:let
let sum=2+3; echo $sum
let i++; echo $i
2. 小数运算
echo "2*4" |bc
echo "2^4" |bc
echo "scale=2;6/4" |bc
awk 'BEGIN{print 1/2}'
echo "print 5.0/2" |python
2.浮点运算:
bash本身不能做小数计算:需要bc命令转换
#echo "2*4" | bc
#echo "2^4" | bc
#echo "scale=2;6/4" | bc
scale: 精度
#awk 'BEGIN{print 1/2}'
#echo "print 5.0/2" | python
计算我的信用卡一个月的利息,假设我欠10000块钱
#!/bin/bash
m=$( echo 5/10000|bc -l)
#因为shell不支持小数,所以要用bc转换一下
sum=10000
for i in {1..365}
do
sum=$(echo $sum+$sum*$m | bc )
echo $sum
done
echo $sum
3.变量引用
转义:\
为了显示元字符,需要引用。当一个字符被引用时,其特殊含义被禁止
把有意义的变的没意义,把没意义的变的有意义
\n \t \r
# echo -e '5\\n6\n7'
5\n6
7
# yum install httpd mysql -y
# yum groupinstall KDE\ Plasma\ Workspaces
完全引用:'' //强引 硬引
部分引用:"" //弱引 软引
#ls -lh --sort=size | tac
#echo hello;world
#echo you own $1250
例子: 从这个例子举出的是,弱引和强引的区别,强引打印原文,弱引打印变量
[root@master ~]# num=15
[root@master ~]# echo 1793班有$num个女生
1793班有15个女生
[root@master ~]# echo "1903班有$num个女生"
1903班有15个女生
[root@master ~]# echo '1903班有$num个女生'
1903班有$num个女生
读取用户标准输入:read 【重点参数】
read:功能就是读取键盘输入的值,并赋给变量
#read -t 5 var
#read -p "提示信息" var
示例:
#!/bin/bash
read system setting network
echo "This is computer function $system"
echo "This is computer function $setting"
echo "This is computer function $network"
示例:2
暂停用户输入:
[root@master ~]# shiyan.sh
read -p "如果你同意以上协议请按回车继续! " answer
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
[root@master ~]# sh shiyan.sh
如果你同意以上协议请按回车继续!
这是下面的操作了
这是下面的操作了
这是下面的操作了
这是下面的操作了
这是下面的操作了
示例3:
[root@master ~]# vim shiyan2.sh
#!/bin/bash
read -p "Do you want to continue [Y/N]? " answer
case $answer in
Y|y)
echo "fine ,continue";;
N|n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
[root@master ~]# sh shiyan2.sh
Do you want to continue [Y/N]? Y
fine ,continue
[root@master ~]# sh shiyan2.sh
Do you want to continue [Y/N]? N
ok,good bye
-s 选项
能够使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是read命令将文本颜色设置成与背景相同的颜色
#!/bin/bash
read -s -p "Enter your password: " pass
echo "your password is $pass"
exit 0
取消屏幕回显:
stty -echo
stty echo
变量长度
[root@master ~]# a=123
[root@master ~]# echo ${#a} # 表示$var的长度
3
变量嵌套
[root@master ~]# a=8
[root@master ~]# name8=9
[root@master ~]# eval echo \$name$a
9
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·~·~~~~~~~~~~~~~~~~~~~~~~~~~~~~
示例4: 【这个脚本参数内容需要熟记,后续经常用到的】
[root@master ~]# vim shiyan4.sh #操作选项脚本,简单实现
#!/bin/bash
echo -e '1.配置yum客户端'
echo 2.添加A记录
echo 3.一键安装lamp环境
echo '4.一键配置静态IP'
read -p "请选择你想使用的功能(1/2/3/4):" num
con_ip(){
echo 这是配置IP地址的小工具
}
case $num in
1):;;
2):;;
3):;;
4)con_ip;;
*):;;
esac
[root@master ~]# sh shiyan4.sh
1.配置yum客户端
2.添加A记录
3.一键安装lamp环境
4.一键配置静态IP
请选择你想使用的功能(1/2/3/4):1
[root@master ~]# sh shiyan4.sh
1.配置yum客户端
2.添加A记录
3.一键安装lamp环境
4.一键配置静态IP
请选择你想使用的功能(1/2/3/4):2
[root@master ~]#
1.7 i++和++i
++在前就是先算++
++在后就是后算++
i++ 和 ++i (了解)
对变量的值的影响:
[root@master ~]# i=1
[root@master ~]# let i++
[root@master ~]# echo $i
2
[root@master ~]# j=1
[root@master ~]# let ++j
[root@master ~]# echo $j
2
对表达式的值的影响:
[root@master ~]# unset i
[root@master ~]# unset j
[root@master ~]# i=1
[root@master ~]# j=1
[root@master ~]# let x=i++ 先赋值,再运算
[root@master ~]# let y=++j 先运算,再赋值
[root@master ~]# echo $i
2
[root@master ~]# echo $j
2
[root@master ~]# echo $x
1
[root@master ~]# echo $y
2
二.shell变成
1.shell条件测试
测试
test 条件
条件为真返回 0,条件为假返回 1 【这个必须要记住,0为真,1为假】
[ 条件 ]
test能够理解3中类型的表达式
1.文件测试
2.字符串比较
3.数字比较
字符串
-n STRING
the length of STRING is nonzero
-z STRING
the length of STRING is zero
STRING1 = STRING2
the strings are equal
STRING1 != STRING2
the strings are not equal
数字 【重点参数】
eq 等于equal ne 不等于
ge 大于等于 gt 大于
le 小于等于 lt 小于
文件
-f 存在且是普通文件
-d 存在且是目录
-l 存在且是符号链接
-b 块设备
-c 字符设备
-e 文件存在
-r
-w
-x
file1 -nt file2 file1 比 file2 新(修改时间)
file1 -ot file2 file1 比 file2 旧(修改时间)
=========
格式1: test 条件表达式
格式2: [ 条件表达式 ]
格式3: [[ 条件表达式 ]]
man test
===文件测试 [ 操作符 文件或目录 ]
[root@master ~]# test -d /home
[root@master ~]# echo $?
0 #零说明改目录存在
[root@master ~]# test -d /home11111
[root@master ~]# echo $?
1 #一说明改目录存在
[root@master ~]# [ -d /home ]
[ -e dir|file ]
[ -d dir ]
[ -f file ] 是否存在,而且是文件
[ -r file ] 当前用户对该文件是否有读权限
[ -x file ]
[ -w file ]
[ -L file ]
[root@master ~]# [ ! -d /ccc ] && mkdir /ccc
[root@master ~]# [ -d /ccc ] || mkdir /ccc
===数值比较 [ 整数1 操作符 整数2 ] 【重点】
[ 1 -gt 10 ] 大于
[ 1 -lt 10 ] 小于
[ 1 -eq 10 ] 等于
[ 1 -ne 10 ] 不等于
[ 1 -ge 10 ] 大于等于
[ 1 -le 10 ] 小于等于
===字符串比较
提示:使用双引号
[root@master ~]# [ $(id -u) -eq 0 ] || echo "必须是超级用户才能执行"
[root@master ~]# [ "$USER" = "root" ];echo $?
0
[root@master ~]# [ "$USER" = "alice" ];echo $?
1
[root@master ~]# [ "$USER" != "alice" ];echo $?
0
[root@master ~]# [ 1 -lt 2 -a 5 -gt 10 ];echo $?
1
[root@master ~]# [ 1 -lt 2 -o 5 -gt 10 ];echo $?
0
[root@master ~]# [[ 1 -lt 2 && 5 -gt 10 ]];echo $?
1
[root@master ~]# [[ 1 -lt 2 || 5 -gt 10 ]];echo $?
0
[root@master ~]# [[ "$USER" =~ ^r ]];echo $? # 使用正则 正则只能使用[[]]
0
==C语言风格的数值比较
[root@master ~]# ((1<2));echo $?
0
[root@master ~]# ((1==2));echo $?
1
[root@master ~]# ((1>2));echo $?
1
[root@master ~]# ((1>=2));echo $?
1
[root@master ~]# ((1<=2));echo $?
0
[root@master ~]# ((1!=2));echo $?
0
[root@master ~]# ((`id -u`>0));echo $?
1
[root@master ~]# (($UID==0));echo $?
0
示例1:
判断变量是不是数字:
[root@master ~]# num1=123
[root@master ~]# num2=sss1414ss
[root@master ~]# [[ "$num1" =~ ^[0-9]+$ ]];echo $? #判断是数字,则成功得出值为0
0
[root@master ~]# [[ "$num1" =~ ^[0-9]+$ ]];echo $? #判断结果为英文加数字。得出结果是错误的
1
示例2:判断是否输入的是一个数值
[root@master ~]# vim shiyan5.sh
#!/bin/bash
#判断用户输入的是否是数字
read -p "请输入一个数值: " num #设立条件
while :
do
if [[ $num =~ ^[0-9]+$ ]];then #if语句进行判断
break
else
read -p "不是数字,请重新输入数值: " num #判断失败则需要冲i重新输入
fi
done
echo "你输入的数字是: $num" #判断成功
[root@master ~]# sh shiyan5.sh #执行脚本
请输入一个数值: 123 #输入数字匹配成功
你输入的数字是: 123
[root@master ~]# sh shiyan5.sh
请输入一个数值: abc #输入的不是数值,则需要重新输入
不是数字,请重新输入数值: 123
你输入的数字是: 123
示例3:判断限定用户输入八位数的数值
[root@master ~]# vim shiyan6.sh
#!/usr/bin/env bash
read -p "其请输入一个8位数数字: " NUM
if [ ${#NUM} -eq 8 ];then
echo "你输入的是一个8位数"
else
echo "你输入的不是8位数字:拜拜嘞,您!"
exit
fi
if [[ ! $NUM =~ ^[0-9]+$ ]];then
echo "你输入的不是8位数字!"
else
echo "你输入的是8位数字: good"
fi
[root@master ~]# sh shiyan6.sh
其请输入一个8位数数字: 123455
你输入的不是8位数字:拜拜嘞,您!
[root@master ~]# sh shiyan6.sh
其请输入一个8位数数字: 14725836
你输入的是一个8位数
你输入的是8位数字: good
需要注意:shell是一门很强大的编程语言,所有的判断条件都可以灵活应用,采用if语句判断发,条件筛选法,以及后续所用到的函数结合法,精通shell可以高效率的完成多数机器自动化部署
2. shell分支if语句
流程控制:if
•在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。
•流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令
•exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,例如:
•exit 0
if语句架构框架: 【必会技能】
单分支结构
if 条件测试
then 命令序列
fi
双分支结构
if 条件测试
then 命令序列
else 命令序列
fi
多分支结构
if 条件测试1
then 命令序列
[elif 条件测试2
then 命令序列
elif 条件测试3
then 命令序列]...
else 命令序列
fi
示例1:
read -p "确认开始安装KVM [y]: " kvm_install
if [ ! "${kvm_install}" = "y" ];then
echo -e "$red_col输入不正确! $reset_col"
exit
fi
当然你也可以这样写,但是这样写不太好看:
if list1;then list2;elif list3;then list4;else list5;fi;
示例2: 编写脚本输出一个数值
[root@master ~]# cat shiyan7.sh
#!/usr/bin/env bash
if [ "$1" = "hello" ]; then #添加hello值
echo "Hello! How are you ?"
elif [ "$1" = "" ]; then #添加空 值
echo "You MUST input parameters"
else
echo "The only accept parameter is hello"
fi
[root@master ~]# sh shiyan7.sh hello #输入变量参数hello数值 则会打印出如下
Hello! How are you ?
[root@master ~]# sh shiyan7.sh #后面未输入参数,则代表输入的是空,则会输出如下内容
You MUST input parameters
示例3: 写出一个输入不同脚本得出不同输入结果的sh文件
[root@master ~]# vim shiyan8.sh
#/usr/bash
echo "Press y to continue"
read yn
if [ "$yn" = "y" ]; then
echo "script is running..."
else
echo "STOP!"
fi
[root@master ~]# sh shiyan8.sh
Press y to continue
y #输入y 得出以下值
script is running...
[root@master ~]# sh shiyan8.sh
Press y to continue
yn #输入yn得出以下值
STOP!
多个条件联合
逻辑与
if [ $condition1 ] && [ $condition2 ]
if [ $condition -a $condition2 ]
if [[ $condition1 && $condition2 ]]
逻辑或
if [ $condition1 ] || [ $condition2 ]
if [ $condition -o $condition2 ]
if [[ $condition1 || $condition2 ]]
case 命令 【这个也是一个重点模式,必须记住】
case 语句是 shell 中流控制的第二种方式,语法如下:
case 变量 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
模式3)
命令序列3
;;
*)
无匹配后命令序列
;;
esac
命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。
示例4: 写出一个多项选项的操作脚本命令,比如abcd四个选项项目
[root@master ~]# cat shiyan9.sh
#!/usr/bin/env bash
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
printf "\e[1;31m Please input your select: \e[0m" && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
setenforce 0 && sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
;;
"c")
yum -y install httpd httpd-tools
echo "httpd is installd"
;;
"d")
exit
;;
*)
printf "请按照上方提供的选项输入!!!\n"
;;
esac
[root@master ~]# sh shiyan9.sh #执行脚本
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
Please input your select: a
[root@master ~]# sh shiyan9.sh
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
Please input your select: b
setenforce: SELinux is disabled
[root@master ~]# vim shiyan10.sh
#!/usr/bin/env bash
printf "\e[1;31m 确定要继续删除吗 yes/no: \e[0m" && read var #\e[1;31m 这个是给字体添加颜色
case $var in
"yes"|"y"|'Y'|"YES")
echo "继续删除"
;;
*)
echo "停止删除"
;;
esac
[root@master ~]# sh shiyan10.sh #执行脚本
确定要继续删除吗 yes/no: yes #选择yes
继续删除
示例:使用函数实现选项条件内容,函数名字可自己随意定义,如hepl () { }
[root@master ~]# vim shiyan10.sh
#!/usr/bin/env bash
help () {
cat <<-EOF
System_tools V1.0
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
EOF
}
help
printf "\e[5;7;31m 确定要继续删除吗 yes/no: \e[0m" && read var
case $var in
"h"|"H")
help
;;
"f"|"F")
fdisk -l
;;
"d"|"D")
df -Th
;;
"m"|"M")
free -h
;;
"u"|"U")
uptime
;;
"q"|"Q")
exit
;;
esac
[root@master ~]# sh shiyan10.sh
确定要继续删除吗 yes/no: yes
继续删除
[root@master ~]# vim shiyan11.sh
[root@master ~]# sh shiyan11.sh #输入得出以下选项内容
System_tools V1.0
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
确定要继续删除吗 yes/no: h
System_tools V1.0
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
案例4:简单的JumpServer
跳板主机
1)mysql1
2)mysql2
3)bj-web1
h) help
q) exit
#跳板主机可以通过脚本实现主机之间的来回切换
#!/usr/bin/env bash
cat <<-EOF
跳板主机
1)master2
2)master3
3)master4
h) help
q) exit
EOF
printf "\e[5;7;31m 请输入密码 : \e[0m" && read pass
if [ $pass = "ppp" ];then
else
echo "password is error" && exit
fi
printf "\e[5;7;31m 请输入你要连接的机器 : \e[0m" && read var
## 先免密和解析
case $var in
1)
ssh master2
;;
2)
ssh master3
;;
3)
ssh master4
;;
h)
echo "you need help!!!"
;;
q)
exit
;;
esac
[root@master ~]# sh shiyan12.sh
跳板主机
1)master2
2)master3
3)master4
h) help
q) exit
请输入密码 : ppp
请输入你要连接的机器 : master2
3. shell循环for于语句
循环次数是固定的
for i in {取值范围}
do
循环体
done
下面用for循坏语句采用的脚本,【实战技术非常强悍,需牢记】
示例1: 1-150数字求和
[root@master ~]# cat shiyan13.sh
#!/usr/bin/env bash
num=0
for i in {1..150} # {1..150}/(1,2,3,4,5, 100)/`seq 100`
do
num=$[$num+$i]
done
echo $num
[root@master ~]# sh shiyan13.sh
11325 #求和的结果
示例2:找出1-20中能被2整除的数字
[root@master ~]# cat sy1.sh
#!/usr/bash
for i in {1..20}
do
a=$[$i%2]
if [ $a -eq 0 ]; then
echo $i
fi
done
[root@master ~]# sh sy1.sh #执行脚本以下都是能被2整除的数字
2
4
6
8
10
12
14
16
18
20
示例3:检测主机存活性,把229网段的主机能ping通的ip全部列出,当然还有一种更简单的方法
采用:nmap -sP 192.168.229.0/24 |grep "report for" #可直接在命令行过滤所有ip
[root@master ~]# cat sy2.sh
#!/usr/bin/env bash
for i in {1..254}
do
( ping -c1 -W1 192.168.229.$i &>/dev/null
if [ $? -eq 0 ]; then
echo "192.168.229.$i is tong"
fi
)&
done
[root@master ~]# sh sy2.sh
192.168.229..1 is tong
192.168.229..2 is tong
192.168.229..51 is tong
192.168.229..53 is tong
192.168.229..54 is tong
192.168.229..52 is tong
192.168.229..61 is tong
192.168.229.100 is tong
示例4:计算文件lxw.txt中数值之和
[root@master ~]# cat lxw.txt
6324346
341241
34632151
34621125
36126
1512616
3461261
126162161253
[root@master ~]# cat sy3.sh
#!/usr/bash
sum=0
for i in `cat lxw.txt` #选择文件绝对路径
do
sum=$[$sum+$i] #采用变量
done
echo $sum #得出结果值
[root@master ~]# sh sy3.sh
126243090119
示例5. C 语言风格的 求1-500之和
[root@master ~]# cat sy4.sh
#!/usr/bash
for (( i=1; i<=500; i++))
do
sum=$[$sum+$i]
done
echo $sum
[root@master ~]# sh sy4.sh
125250
4.shell循环while语句
循环次数不一定是固定的
可以固定
可以不固定
while循环语句 框架如下:
while 条件
do
循环体
done
==当条件测试成立(条件测试为真),执行循环体
##完善系统工具的输出及操作性
#!/usr/bin/env bash
while 1>0
do
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
printf "\e[1;35m Please input your select: \e[0m" && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
;;
"c")
yum -y install httpd httpd-tools
;;
"d")
exit
;;
*)
printf "请按照上方提供的选项输入!!!\n"
;;
esac
if [ $? -eq 0 ];then
clear
else
printf "\e[1;31m Warning: Your program exist ERROR!!! \e[0m\n"
break
fi
done
示例:判断是否是字母
[root@master ~]# vim sy2.sh
#!/usr/bin/env bash
read -p "请输入一个字母" var
if [ ${#var} -eq 1 ]; then
echo "$var"
else
echo "$var 不是一个字母"
exit 4
fi
[root@master ~]# sh sy2.sh
请输入一个字母123
123 不是一个字母
[root@master ~]# sh sy2.sh
请输入一个字母abc
abc 不是一个字母
[root@master ~]# sh sy2.sh
请输入一个字母a
a
示例:用while循环打印出乘法表格
#!/usr/bin/env bash
num=9
row=1
while [ $row -le $num ] ;do
col=1
while [ $col -le $row ] ;do
printf "$row*$col=$[$row*$col]\t"
col=$[col+1]
done
echo
row=$[row+1]
done
[root@master ~]# sh sy7.sh
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
[root@master ~]# cat sy9.sh
#!/usr/bash
i=1
while [ $i -le 16 ]
do
n=16
while [ $n -ge $i ]
do
echo -n " "
n=$[$n-1]
done
m=1
while [ $m -le $i ]
do
echo -n "* "
m=$[$m+1]
done
echo
i=$[$i+1]
done
[root@master ~]# sh sy9.sh
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
* * * * * * * * * *
* * * * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * * * *
* * * * * * * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
6.shell循环until语句
循环次数不一定是固定的
可以固定
可以不固定
until 条件
do
循环体
done
==当条件测试成立(条件测试为假),执行循环体
[root@host ~]# cat until.sh
#!/bin/bash
x=1
until [ $x -ge 8 ]
do
echo $x
x=`expr $x + 1`
done
x=1
while [ ! $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
[root@master ~]# sh sy6.sh #执行脚本 结果打印为1-7 1-9
1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
9
7.shell循环控制shift、continue、break、exit
shift命令
位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。
对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell 程序不知道其个数时,可以把所有参数一起赋值给变量$*。
若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1
示例1:
[root@master ~]# cat sy10.sh
#!/usr/bin/env bash
a=1
until [ $# -eq 0 ]
do
echo "第$a个参数为: $1 参数个数为: $#"
shift
a=$[a+1]
done
[root@master ~]# sh sy10.sh 1 2 3 4 5 a
第1个参数为:1参数个数为:6
第2个参数为:2参数个数为:5
第3个参数为:3参数个数为:4
第4个参数为:4参数个数为:3
第5个参数为:5参数个数为:2
第6个参数为:a参数个数为:1
从上可知 shift 命令每次执行一次,变量的个数($#)减一,而变量值提前一位
示例2:用 until 和 shift 命令计算所有命令行参数的和。
[root@master ~]# cat sy12.sh
#!/usr/bash
sum=0
until [ $# -eq 0 ]
do
sum=`expr $sum + $1`
shift
done
echo "sum is: $sum"
[root@master ~]# sh sy12.sh 89 999 4564 5644
sum is: 11296 #求出的总和
Shift 命令还有另外一个重要用途:
Bash 定义了9个位置变量,从 $1 到 $9,这并不意味着用户在命令行只能使用9个参数,借助 shift 命令可以访问多于9个的参数。
Shift 命令一次移动参数的个数由其所带的参数指定。例如当 shell 程序处理完前九个命令行参数后,可以使用 shift 9 命令把 $10 移到 $1。
8.Linux脚本中的break continue exit return
Linux脚本中的break continue exit return 【下面的参数能记住多少算多少,这些都是非常使用的参数】
break
结束并退出循环
continue
在循环中不执行continue下面的代码,转而进入下一轮循环
exit
退出脚本,
常带一个整数给系统,如 exit 0
return
在函数中将数据返回
或返回一个结果给调用函数的脚本
可理解为:break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本
例如:
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
[root@master shell]# cat sy1.sh #这个脚本加了continue,不会跳出循坏,输入比5大的数值即可跳出循环!
#!/usr/bash
while :
do
echo -n "input a number between 1 to 5:"
read anum
case $anum in
1|2|3|4|5) echo "you number is $anum!"
;;
*) echo "you do not select a number between 1 to 5,game is over!"
break
;;
esac
done
continue
[root@master shell]# sh sy1.sh
input a number between 1 to 5:1
you number is 1!
input a number between 1 to 5:2
you number is 2!
input a number between 1 to 5:3
you number is 3!
input a number between 1 to 5:4
you number is 4!
input a number between 1 to 5:6
you do not select a number between 1 to 5,game is over
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
示例: 对上面的例子进行修改:添加continue这个参数就可以陷入无限循环!!!!
[root@master shell]# vim sy2.sh
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5!"
continue
echo "Game is over!"
;;
esac
done
[root@master shell]# sh sy2.sh
Input a number between 1 to 5: 1
Your number is 1!
Input a number between 1 to 5: 2
Your number is 2!
Input a number between 1 to 5: 3
Your number is 3!
Input a number between 1 to 5: 4
Your number is 4!
Input a number between 1 to 5: ^C
[root@master shell]#
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句
代码如下:
echo "Game is over!"
#实战--shell版本jumpserver开发 模板,测试自己的搭建shell脚本思路
#!/usr/bin/env bash
#
# Author:
可以先添加上账密验证环节
while :
do
trap ':' INT EXIT TSTP TERM HUP
clear
cat <<-EOF
+-------------------------------------+
| JumpServer @Version1.0 |
+-------------------------------------+
| a. WebServer Apache. |
| b. MySQL Databases Server. |
| c. PHP Development Computer. |
| d. Quit |
+-------------------------------------+
EOF
read -p "Please input your jump to server's number: " computer
case $computer in
a)
ssh jumper@192.168.229.62 //可以嵌套子case循环
;;
b)
ssh jumper@192.168.229.63
;;
c)
ssh jumper@192.168.229.64
;;
d)
exit
;;
*)
printf "ERROR: Please redo your select!"
;;
esac
done
三.shell函数与正则 【重中之重高级进阶版】
1.shell编程之函数
函数,是搭建整个shell脚本的一个强大得多功能,之前我们学的都是如何搭建局部模式,正如盖一栋房子,只把边边角角建筑好,接下来就需要各大部门的协调整合才能进行一个竣工验收的效果,函数正式如此!
function (功能) 功能函数
定义函数
调用函数
取消函数
函数传参
命名空间
local
返回值
return value
value不能超过0-255
shell函数function
1.函数声明
function_name () {
list of commands
}
函数名 function_name,这就是你将使用它从其他地方在你的脚本调用。
取消函数
unset myfunc //取消函数
示例1:通过函数调取字符值
[root@master shell]# cat sy3.sh
#!/usr/bash
myfunc() #添加函数,可随意定义
{
echo “This is my first shell function”
}
myfunc #调用函数,这一步必须有,比如你添加是个函数就得在最后调用十个函数
[root@master shell]# sh sy3.sh
“This is my first shell function”
2.函数传参
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数
示例:
[root@master shell]# cat sy4.sh
#!/bin/bash
funParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funParam 1 2 3 4 5 6 7 8 9 34 73
[root@master shell]# sh sy4.sh #把函数参数进行参数传递调用
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
2.shell编程之数组
数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似)。
与大部分编程语言类似,数组元素的下标由0开始。
数组、数组名称、数组成员(a[0])、下标、数组成员值
数组定义:
[root@master shell]# cat sy5.sh
#!/usr/bash
declare -a myarry=(5 6 7 8)
echo ${myarry[3]} #这里选择数组从上面数字6开始为第一个,选择3则是第8个,所以输出结果等于8
[root@master shell]# sh sy5.sh
8
定义方法2:
# array=( one two three four five six )
# array2=(tom jack alice)
# array3=(`cat /etc/passwd`) 将该文件中的每一个行作为一个元素赋值给数组array3
# array4=(`ls /var/ftp/Shell/for*`)
# array5=(tom jack alice "bash shell")
# colors=($red $blue $green $recolor) //先给变量赋值 或者重新定义数组
# array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack) //直接指定下标位置,数组下标不一定连续,可以为空
定义方法3:
#!/bin/bash
area[11]=23
area[13]=37
area[51]="UFOs"
# 数组成员不必一定要连贯或连续的.
# 数组的一部分成员允许不被初始化.
# 数组中空缺元素是允许的.
访问数组
当您设置任何数组变量,并可访问它,如下所示:
${array_name[index]}
示例:
[root@master shell]# cat sy6.sh
#!/bin/sh
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "First Index: ${NAME[0]}" #这里是可以调用函数数组比如0对应BJ
echo "Second Index: ${NAME[1]}"
[root@master shell]# sh sy6.sh
First Index: BJ
Second Index: SH
您可以访问数组中的所有项目通过以下方式之一:
${array_name[*]}
${array_name[@]}
[root@master shell]# cat sy7.sh
#!/usr/bin
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "First Index: ${NAME[*]}" #调取所有
echo "Second Index: ${NAME[@]}"
[root@master shell]# sh sy7.sh
First Index: BJ SH SZ GZ HZ
Second Index: BJ SH SZ GZ HZ
'疑难点
shell数组中"*" 和 "@" 区别
关于在shell脚本中数组变量中 “*”跟 “@” 区别
“*”当变量加上“” 会当成一串字符串处理.
“@”变量加上“” 依然当做数组处理.
在没有加上“” 的情况下 效果是等效的.'
示例:
[root@master shell]# cat sy8.sh
#!/usr/bin/env bash
array=('gz' 'cloud' '19')
echo "case 1"
for line in "${array[@]}"
do
echo $line
done
echo "case 2"
for line in "${array[*]}"
do
echo $line
done
echo "case 3"
for line in ${array[*]}
do
echo $line
done
echo "case 4"
for line in ${array[@]}
do
echo $line
done
[root@master shell]# sh sy8.sh
case 1
gz
cloud
19
case 2
gz cloud 19
case 3
gz
cloud
19
case 4
gz
cloud
19
3. 正则表达式RE [重点正则表达式,必会技能]
正则表达式基本元字符
正则表达式拓展元字符
正则表达式(regular expression, RE)是一种字符模式, 用于在查找过程中匹配指定的字符. 在大多数程序里, 正则表达式都被置于两个正斜杠之间;
例如/l[oO]ve/就是由正斜杠界定的正则表达式, 它将匹配被查找的行中任何位置出现的相同模式. 在正则表达式中,元字符是最重要的概念
正则表达式与通配符:
1.正则表达式一般用于处理文本内容,常用命令有grep,sed,awk,vim等
通配符一般用于匹配文件名,常用命令有find,ls,cp等
2.各符号的含义不尽相同.
什么地方使用正则表达式
vim grep sed awk nginx apache mail垃圾邮件过滤。。。 perl java python 等等都使用正则
构成
1.元字符(基本元字符、扩展元字符)
2.除元字符之外的任意字符都是表示他字面意思的正则表达式
正则表达式的匹配过程
正则表达式是按照从表达式最左端第一个字符开始,左到右依次一个一个字符进行匹配.当字符串中有字符成功匹配到正则表达式中字符,则从这个位置开始尝试正则表达式中的下一个字符进行匹配,如果匹配成功则继续从这个位置开始匹配正则表达式中下一个字符;如果匹配不成功,则“回溯”到第一次匹配的字符处重新从正则表达式中第一个字符开始匹配。
No.1 正则表达式基本元字符 【以下所有的参数最好能记住多少记住多少,在企业实战是最能体现工作效益的】
基本正则表达式元字符
元字符 功能 示例
^ 行首定位符 ^love
$ 行尾定位符 love$
. 匹配单个字符 l..e
* 匹配前导符0到多次 ab*love
.* 任意多个字符
[] 匹配方括号中的任意一个字符 [lL]ove
[ - ] 匹配指定范围内的一个字符 [a-z0-9]ove
[^] 匹配不在指定组内的字符 [^a-z0-9]ove
\ 用来转义元字符 love\.
\< 词首定位符 \<love
\> 词尾定位符 love\>
:% s/172.16.130.1/172.16.130.5/
:% s/\(172.16.130.\)1/\15/
:% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/
:3,9 s/\(.*\)/#\1/
x\{m\} 字符x重复出现m次 o\{5\}
x\{m,\} 字符x重复出现m次以上 o\{5,\}
x\{m,n\} 字符x重复出现m到n次 o\{5,10\}
No.2 正则表达式拓展元字符
powershell
扩展正则表达式元字符
+ 匹配一个或多个前导字符 [a-z]+ove
? 匹配零个或一个前导字符 lo?ve
a|b 匹配a或b love|hate
() 组字符loveable|rs love(able|rs) ov+ ov+ (ov)+
(..)(..)\1\2 标签匹配字符 (love)able\1er
x{m} 字符x重复m次 o{5
x{m,} 字符x重复至少m次 o{5,}
x{m,n} 字符x重复m到n次 o{5,10}
编写正则表达式的3 个步骤:
1 知道要匹配的内容以及它如何出现在文本中。
2 编写一个模式来描述要匹配的内容
3 测试模式来查看它匹配的内容,不能错,不能漏,不能多
shell文本处理三剑客 【重点戏文本处理三剑客】
1.正则表达式RE
正则表达式RE
========================================================
重要的文本处理工具:vim、sed、awk、grep
一、什么是正则表达式?
正则表达式(regular expression, RE)是一种字符模式,用于在查找过程中匹配指定的字符。
在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/l[oO]ve/就是由正斜杠界定的正则表达式,它将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念。
匹配数字: ^[0-9]+$ 123 456 5y7
匹配Mail: [a-z0-9_]+@[a-z0-9]+\.[a-z]+ tigerfive2010@126.com
匹配IP: [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
或
[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}
[root@master shell]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=172.16.100.1
NETMASK=255.255.255.0
GATEWAY=172.16.100.254
[root@master shell]# egrep '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=172.16.100.1
NETMASK=255.255.255.0
GATEWAY=172.16.100.254
二、元字符
定义:元字符是这样一类字符,它们表达的是不同于字面本身的含义
shell元字符(也称为通配符) 由shell来解析,如rm -rf *.pdf,元字符* Shell将其解析为任意多个字符
正则表达式元字符 由各种执行模式匹配操作的程序来解析,比如vi、grep、sed、awk、python
[root@master shell]# rm -rf *.pdf
[root@master shell]# grep 'abc*' /etc/passwd
abrt:x:173:173::/etc/abrt:/sbin/nologin
vim示例:
:1,$ s/tom/David/g //如tom、anatomy、tomatoes及tomorrow中的“tom”被替换了,而Tom确没被替换
:1,$ s/\<[Tt]om\>/David/g
1. 正则表达式元字符:
===基本正则表达式元字符
元字符 功能 示例
========================================================
^ 行首定位符 ^love
$ 行尾定位符 love$
. 匹配单个字符 l..e
* 匹配前导符0到多次 ab*love
.* 任意多个字符
[] 匹配指定范围内的一个字符 [lL]ove
[ - ] 匹配指定范围内的一个字符 [a-z0-9]ove
[^] 匹配不在指定组内的字符 [^a-z0-9]ove
\ 用来转义元字符 love\.
\< 词首定位符 \<love
\> 词尾定位符 love\>
\(..\) 匹配稍后使用的字符的标签 :% s/172.16.130.1/172.16.130.5/
:% s/\(172.16.130.\)1/\15/
:% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/
:3,9 s/\(.*\)/#\1/
x\{m\} 字符x重复出现m次 o\{5\}
x\{m,\} 字符x重复出现m次以上 o\{5,\}
x\{m,n\} 字符x重复出现m到n次 o\{5,10\}
===扩展正则表达式元字符
+ 匹配一个或多个前导字符 [a-z]+ove
? 匹配零个或一个前导字符 lo?ve
a|b 匹配a或b love|hate
() 组字符 loveable|rs love(able|rs) ov+ ov+ (ov)+
(..)(..)\1\2 标签匹配字符 (love)able\1er
x{m} 字符x重复m次 o{5}
x{m,} 字符x重复至少m次 o{5,}
x{m,n} 字符x重复m到n次 o{5,10}
2. POSIX字符类:
表达式 功能 示例
[:alnum:] 字母与数字字符 [[:alnum:]]+
[:alpha:] 字母字符(包括大小写字母) [[:alpha:]]{4}
[:blank:] 空格与制表符 [[:blank:]]*
[:digit:] 数字字母 [[:digit:]]?
[:lower:] 小写字母 [[:lower:]]{5,}
[:upper:] 大写字母 [[:upper:]]+
[:punct:] 标点符号 [[:punct:]]
[:space:] 包括换行符,回车等在内的所有空白 [[:space:]]+
正则匹配示例:vim
/love/
/^love/ love开头的行
/love$/ love结尾的行
/l.ve/ l和ve间可以存在任意一个字符
/lo*ve/ l和ve间可以存在任意多个o
/[Ll]ove/ Love love
/love[a-z]/ lovea loveb .. lovez
/love[^a-zA-Z0-9]/ love_ love/
/.*/ 整行
/^$/ 空行 /^[ ]*$/
/^[A-Z]..$/ 一个大写字母加两个任意字符的行
/^[A-Z][a-z]*3[0-5]/
/[a-z]*\./ 任意多个小写字母加上一个.
/^ *[A-Z][a-z][a-z]$/
/^[A-Za-z]*[^,][A-Za-z]*$/
/\<fourth\>/
/\<f.*th\>/ f为词首 th为词尾的任意单词
/5{2}2{3}\./ 55222.
/^$/ 空行
/^[ \t]*$/ 空行
注释行
/^#/
/^[ \t]*#/
:1,$ s/\([Oo]ccur\)ence/\1rence/
:1,$ s/\(square\) and \(fair\)/\2 and \1/
2.shell变成之grep
grep家族
========================================================
grep: 在文件中全局查找指定的正则表达式,并打印所有包含该表达式的行
egrep: 扩展的egrep,支持更多的正则表达式元字符
fgrep: 固定grep(fixed grep),有时也被称作快速(fast grep),它按字面解释所有的字符
一、grep命令格式
grep [选项] PATTERN filename filename ...
# grep 'Tom' /etc/passwd
# grep 'bash shell' /etc/test
找到: grep返回的退出状态为0
没找到: grep返回的退出状态为1
找不到指定文件: grep返回的退出状态为2
grep 程序的输入可以来自标准输入或管道,而不仅仅是文件,例如:
# grep 'tom'
# ps aux |grep 'sshd'
# ll |grep '^d'
# grep 'alice' /etc/passwd /etc/shadow /etc/group
二、grep使用的元字符
grep: 使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}, \+, \|
egrep(或grep -E): 使用扩展元字符集 ?, +, { }, |, ( )
注:grep也可以使用扩展集中的元字符,仅需要对这些元字符前置一个反斜线
\w 所有字母与数字,称为字符[a-zA-Z0-9] 'l[a-zA-Z0-9]*ve' 'l\w*ve'
\W 所有字母与数字之外的字符,称为非字符 'love[^a-zA-Z0-9]+' 'love\W+'
\b 词边界 '\<love\>' '\blove\b'
三、grep 示例
grep -E 或 egrep
# egrep 'NW' datafile
# egrep 'NW' d*
# egrep '^n' datafile
# egrep '4$' datafile
# egrep TB Savage datafile
# egrep 'TB Savage' datafile
# egrep '5\..' datafile
# egrep '\.5' datafile
# egrep '^[we]' datafile
# egrep '[^0-9]' datafile
# egrep '[A-Z][A-Z] [A-Z]' datafile
# egrep 'ss* ' datafile
# egrep '[a-z]{9}' datafile
# egrep '\<north' datafile
# egrep '\<north\>' datafile
# egrep '\<[a-r].*n\>' datafile
# egrep '^n\w*\W' datafile
# egrep '\bnorth\b' datafile
# egrep 'NW|EA' datafile
# egrep '3+' datafile
# egrep '2\.?[0-9]' datafile
# egrep '(no)+' datafile
# egrep 'S(h|u)' datafile
# egrep 'Sh|u' datafile
三、grep选项
-i, --ignore-case 忽略大小写
-l, --files-with-matches 只列出匹配行所在的文件名
-n, --line-number 在每一行前面加上它在文件中的相对行号
-c, --count 显示成功匹配的行数
-s, --no-messages 禁止显示文件不存在或文件不可读的错误信息
-q, --quiet, --silent 静默--quiet, --silent
-v, --invert-match 反向查找,只显示不匹配的行
-R, -r, --recursive 递归针对目录
--color 颜色
-o, --only-matching 只显示匹配的内容
-B, --before-context=NUM print NUM lines of leading context
-A, --after-context=NUM print NUM lines of trailing context
-C, --context=NUM print NUM lines of output context
示例:
[root@master ~]# egrep 'ifcfg' /etc/* 文件
[root@master ~]# grep -R 'ifcfg' /etc 目录
[root@master ~]# egrep 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd:root:x:0:0:root:/root:/bin/bash
/etc/passwd:operator:x:11:0:operator:/root:/sbin/nologin
/etc/shadow:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUYfw7vJW.78.uRXCLIxw4mBk82Z99:7:::
[root@master ~]# egrep -l 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd
/etc/shadow
[root@master ~]# egrep -n 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd:1:root:x:0:0:root:/root:/bin/bash
/etc/passwd:11:operator:x:11:0:operator:/root:/sbin/nologin
/etc/shadow:1:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUy8.M78.uRXCLIxw4mBk82ZrNlxyf54
[root@master ~]# egrep '54:04:A6:CE:C2:1F' /etc/sysconfig/*
[root@master ~]# egrep 'ifcfg' /etc/* 文件
[root@master ~]# grep -R 'ifcfg' /etc 目录
[root@master ~]# egrep -R '54:04:A6:CE:C2:1F' /etc/sysconfig/
[root@master ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
192.168.2.254
[root@master ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
192.168.2.254
[root@master ~]# grep -r 'ifcfg' ./* -l #谨慎使用
[root@master ~]# grep --help |grep '\-R'
-R, -r, --recursive equivalent to --directories=recurse
[root@master ~]# grep --help |egrep -A5 '\-R'
shell编程之SED [强调实战必备技能之一]
文本的操作
sed是一个“非交互式的”面向字符流的编辑器。
awk是一种负责模式匹配的程序设计语言,,的典型示例是将数据转换成格式化的报表。
sed stream editor
是一种文本编辑器,默认情况下是不会修改原文件的。
也是一种非交互式的编辑器
工作原理
sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区
中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容
送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
语法:
sed [选项] '行的定位 动作' 文件...
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
注:
sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0
只有当命令存在语法错误时,sed的退出状态才是非0
支持正则表达式
与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是
括在斜杠间的模式,用于查找和替换,以下是sed支持的元字符。
使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}
使用扩展元字符集 ?, +, { }, |, ( )
使用扩展元字符的方式:
\+
sed -r
==地址(定址)
模式:
1. 空模式,表示所有的行都执行动作
2. 以行号作为模式
1). 单独的行号
如:1 就是处理第1行
$ 处理最后一行
2). 起始行,结束行
如:1,5 处理第1到5行
3). 起始行~步长
每隔多少行操作一次
如:2~2 从第2行开始,每隔1行
4). 起始位置,+N
表示从起始位置开始,后面的N行都进行处理
如:3,+5 处理3-8行
3. 以正则作为模式
1). /正则表达式/
如:/^root/
2). /正则表达式1/,/正则表达式2/
表示从第一次匹配到正则表达式1开始到第一次匹配到正则表达式2之间的所有行
如:/^bin/,/sh$/
==动作
动作-----处理命令: ! 非 : 放在命令前面表示取反
1. d 删除 delete
2. p 打印 print
3. r 读取 read
4. w 写 write
5. a 追加 在匹配的行下面插入内容 append
6. i 插入 在匹配行的上一行插入内容 insert
7. c 修改 本行替换 change
8. y 转换的命令,一一对应转换
9. n 处理下一行 next
10. q 退出,不会再向模式空间读入新的行 quit
11. s 查找替换
'模式s/旧的内容(正则表达式)/替换内容(新的内容)/[修饰符]'
修饰符:
g:全局替换
n:n为数字,1-512 替换第n个匹配到的内容
p:打印 -n
w:把处理过的行写入到另一个文件
12. l 列出非打印字符
扩展:
h 把模式空间里的内容复制到暂存缓冲区(覆盖)
H 把模式空间里的内容追加到暂存缓冲区
g 取出暂存缓冲区的内容,将其复制到模式空间,覆盖该处原有内容
G 取出暂存缓冲区的内容,将其复制到模式空间,追加在原有内容后面
x 交换暂存缓冲区与模式空间的内容
\u upper 大写字母
\l lower 小写字母
将文件中所有小写字母替换成大写字母:
# sed 's/[a-z]/\u&/g' /tmp/pass
==选项:
-n:静默输出,关闭模式空间的输出,不会输出未匹配到的行 一般与p命令结合使用
-e:允许进行多项编辑,也就是说对同一行做多次处理、. 可以做多点编辑
-e '动作1' -e '动作2' == '动作1;动作2'
-f: 后接sed脚本,指定执行的sed脚本(将模式动作写到文件中)
-r:允许使用扩展正则
-i:直接作用于原文件 没有输出 在使用-i之前一定先不加-i看看效果
-i.bak:修改原文件之前进行备份
控制流
! 命令取反 例: 1!d 删除第一行以外的行
{} 命令组合 命令用分号分隔 {1h;G} 可以理解为 -e 参数的另一种写法
= 打印行号(输入行的号码,而非处理的次数行号) 例如: sed -n '2{=;p}' infile
n 读入下一行到模式空间 例:'4{n;d}' 删除第5行
N 追加下一行到模式空间.
P 输出多行模式空间的第一部分,直到第一个嵌入的换行符为止。在执行完脚本的最后一个命令之后,模式空间的内容自动输出。P命令经常出现在N命令之后和D命令之前。
d: 删除pattern中的所有⾏行,并读入下一新行到P中 [P来表示模式空间,M来表示保持空间]
D:D 删除M ,P中的第一行,不读入下一行 [P来表示模式空间,M来表示保持空间]
这三个命令能建立一个输入. 输出循环,用来维护两行模式空间,但是一次只输出一行。
这个循环的目的是只输出模式空间的第一行,然后返回到脚本的顶端将所有的命令应用于模式空间的第二行。没有这个循环,当执行脚本中的最后一个命令时,模式空间中的这两行都将被输出。
#### sed命令示例
删除命令:d
# sed -r '3d' datafile
# sed -r '3{d;}' datafile
# sed -r '3{d}' datafile
# sed -r '3,$d' datafile
# sed -r '$d' datafile
# sed -r '/north/d' datafile
# sed -r '/sout/d' datafile
替换命令:s
# sed -r 's/west/north/g' datafile
# sed -r 's/^west/north/' datafile
# sed -r 's/[0-9][0-9]$/&.5/' datafile //&代表在查找串中匹配到的内容
# sed -r 's/Hemenway/Jones/g' datafile
# sed -r 's/(Mar)got/\1ianne/g' datafile
# sed -r 's#3#88#g' datafile
读文件命令:r
# sed -r '/Suan/r /etc/newfile' datafile
# sed -r '2r /etc/hosts' a.txt
# sed -r '/2/r /etc/hosts' a.txt
写文件命令:w
# sed -r '/north/w newfile' datafile
# sed -r '3,$w /new1.txt' datafile
追加命令:a
# sed -r '2a\1111111111111' /etc/hosts
# sed -r '2a\1111111111111\
> 222222222222\
> 333333333333' /etc/hosts
插入命令:i
# sed -r '2i\1111111111111' /etc/hosts
# sed -r '2i111111111\
> 2222222222\
> 3333333333' /etc/hosts
修改命令:c
# sed -r '2c\1111111111111' /etc/hosts
# sed -r '2c\111111111111\
> 22222222222\
> 33333333333' /etc/hosts
获取下一行命令:n
# sed -r '/eastern/{ n; d }' datafile
# sed -r '/eastern/{ n; s/AM/Archile/ }' datafile
暂存和取用命令:h H g G
# sed -r '1h;$G' /etc/hosts
# sed -r '1{h;d};$G' /etc/hosts
# sed -r '1h; 2,$g' /etc/hosts
# sed -r '1h; 2,3H; $G' /etc/hosts
暂存空间和模式空间互换命令:x
# sed -r '4h; 5x; 6G' /etc/hosts
反向选择: !
# sed -r '3d' /etc/hosts
# sed -r '3!d' /etc/hosts
多重编辑选项:-e
# sed -r -e '1,3d' -e 's/Hemenway/Jones/' datafile
# sed -r '1,3d; s/Hemenway/Jones/' datafile
# sed -r '2s/WE/1000phone/g; 2s/Gray/YYY/g' datafile
# sed -r '2{s/WE/1000phone/g; s/Gray/YYY/g}' datafile
七、sed常见操作
删除配置文件中#号注释行
sed -ri '/^#/d' file.conf
sed -ri '/^[ \t]*#/d' file.conf
删除配置文件中//号注释行
sed -ri '\Y^[ \t]*//Yd' file.conf
删除无内容空行
sed -ri '/^[ \t]*$/d' file.conf
删除注释行及空行:
# sed -ri '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# sed -ri '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# sed -ri '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf
修改文件:
# sed -ri '$a\chroot_local_user=YES' /etc/vsftpd/vsftpd.conf
# sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
# sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
# sed -ri '/GSSAPIAuthentication/cGSSAPIAuthentication no' /etc/ssh/sshd_config
给文件行添加注释:
# sed -r '2,6s/^/#/' a.txt
# sed -r '2,6s/(.*)/#\1/' a.txt
# sed -r '2,6s/.*/#&/' a.txt &匹配前面查找的内容
示例二:
示例文件
file1.txt
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA
用Massachusetts替换MA:
#sed 's/MA/Massachusetts/' file1.txt
使用多重指令:
# sed 's/ MA/, Massachusetts/ ; s/ PA/, Pennsylvania/' file1.txt
或者:
#sed -e's/ MA/, Massachusetts/' -e's/ PA/, Pennsylvania/' file1.txt
使用脚本文件:
脚本:namestate
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/
$ sed -f namestate file1.txt
保存输出:
$ sed -f namestate file1.txt > newfile.txt
阻止输入行自动显示:
$ sed -n 's/MA/Massachusetts/p' file1.txt
常见出错信息:
‘’不匹配
s/src/dst/ 缺少最后的“/”
sed流编辑器用法及解析
sed: stream editor(流编辑器)的缩写. 它们最常见的用法是进行文本的替换.
[root@master ~]# sed '1d' passwd //删除文件的第1行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# sed '1,2d' passwd //删除文件的第1,2行
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# cat e.txt
/etc/abc/456
etc
[root@master ~]# sed -r 's#/etc/abc#/var/lib#' e.txt
/var/lib/456
etc
[root@master ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# sed '2,$d' passwd //删除第2行到最后一行s
root:x:0:0:root:/root:/bin/bash
[root@master ~]# sed '/root/d' passwd //匹配到root,删除此行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# sed '/root/,2d' passwd //匹配到root行,到此行的第2行
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# cat -n passwd
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
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
[root@master ~]# sed '1~2d' passwd //删除奇数行
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin
[root@master ~]# sed '0~2d' passwd //删除偶数行
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
1. sed可以从stdin中读取内容
$ cat filename | sed 's/pattern/replace_string/'
2. 选项-i会使得sed用修改后的数据替换原文件
$ sed -i 's/pattern/replace_string/' filename
3. g标记可以使sed执行全局替换
$ sed 's/pattern/replace_string/g' filename
4. g标记可以使sed匹配第N次以后的字符被替换
$ echo "thisthisthisthis" | sed 's/this/THIS/2g'
5. sed中的分隔符可以替换成别的字符, 因为s标识会认为后面的字符为分隔符
$ sed 's:text:replace_text:'
$ sed 's|text|replace_text|'
6. sed可以利用指令来删除文件中的空行
$ sed '/^$/d' filename
7. 替换指定的字符串或数字
$ cat sed_data.txt
11 abc 111 this 9 file contains 111 11 99 numbers 0000
$ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
$ cat sed_data.txt
11 abc NUMBER this 9 file contains NUMBER 11 99 numbers 0000
8. 由于在使用-i参数时比较危险, 所以我们在使用i参数时在后面加上.bak就会产生一个备份的文件,以防后悔
$ sed -i.bak 's/pattern/replace_string/' filename
9. sed如果在脚本中使用的话, 不可避免的要调用变量, 所以以下这种方式可以用来调用变量即‘’换成了“”
$ text=hello
$ echo "hello world" | sed "s/$text/HELLO/"
10. 模式空间保持空间示例
1. 在每行下面添加一个空行
sed 'G' a.sh
2. 将第一行换到最后一行
sed -r '1{h;d};$G' a.sh
3. 交换第一行和第二行内容
sed -r '1{h;d};2G' a.sh
4. 将第一行复制到每个偶数行下面
sed '1h;2~2G;' a.sh
5. 交换第一行和第三行的内容
sed '1{h;d};2{x;H;d};3G' a.sh
6. 用sed实现tac的效果
sed '1!G;h;$!d' a.sh
7. 实现行列转化
sed -r 'H;${x;s/\n/ /g};$!d' a.sh
sed -n 'H;${x;s/\n/ /g;p}' a.sh
sed -n '1p' aa.txt
sed '' aa.txt
sed -n '$p' aa.txt
sed -n '1,4p' aa.txt
sed -n '1~2p' aa.txt
sed -n '2~2p' aa.txt
sed -n '2,+3p' aa.txt
sed -n '/Alice/p' aa.txt
sed -n '/A/p' aa.txt
sed -n '/A.*/p' aa.txt
sed -n '/A$/p' aa.txt
sed -n '/MA$/p' aa.txt
sed -n '/MA$/,/VA$/p' aa.txt
sed -n '/MA$/,/VA$/!p' aa.txt
sed 's/[a-z]/\u&/g' aa.txt
sed -n '/Alice/n;p' aa.txt
sed -n '1~2n;p' aa.txt
sed -n '1~2p' aa.txt
sed -n '6q;p' aa.txt
sed -n 'p;6q' aa.txt
sed -n 'p;6r /etc/hosts' aa.txt
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
sed -in 'p;1r /etc/hosts' aa.txt
sed '1d' aa.txt
sed '1!d' aa.txt
sed -r 's/\<[0-9][0-9]\>/&.5/' aa.txt
sed -nr '3{n;p}' aa.txt
sed -nr '3n;p' aa.txt
暂存和取用命令:h H g G
# sed -r '1h;$G' /etc/hosts
# sed -r '1{h;d};$G' /etc/hosts
# sed -r '1h; 2,$g' /etc/hosts
# sed -r '1h; 2,3H; $G' /etc/hosts
暂存空间和模式空间互换命令:x
# sed -r '4h; 5x; 6G' /etc/hosts
sed -r '1h;2,$g' aa.txt
sed -r '1h;2,$G' aa.txt
sed -r '1H;2,$g' aa.txt
sed -r '1H;2,$G' aa.txt
sed -r '1g;$x' aa.txt
sed -r '1h;$x' aa.txt
sed -r '4h; 5x; 6G' aa.txt
1. 在每行下面添加一个空行
sed 'G' a.sh
2. 将第一行换到最后一行
sed -r '1{h;d};$G' a.sh
3. 交换第一行和第二行内容
sed -r '1{h;d};2G' a.sh
4. 将第一行复制到每个偶数行下面
sed '1h;2~2G;' a.sh
5. 交换第一行和第三行的内容
sed '1{h;d};2{x;H;d};3G' a.sh
6. 用sed实现tac的效果
sed '1!G;h;$!d' a.sh
7. 实现行列转化
sed -r 'H;${x;s/\n/ /g};$!d' a.sh
sed -n 'H;${x;s/\n/ /g;p}' a.sh
shell编程之AWK
awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息
awk处理过程: 依次对每一行进行处理,然后输出
默认分隔符是空格或者tab键
awk简介
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个
或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix
下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定
模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(
屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字
母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的
GNU版本,它提供了Bell实验室和GNU的一些扩展。
awk的两种形式语法格式
awk [options] 'commands' filenames
awk [options] -f awk-script-file filenames
==options:
-F 定义输入字段分隔符,默认的分隔符是空格或制表符(tab)
BEGIN{} {} END{}
行处理前 行处理 行处理后
[root@master ~]# awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}' /etc/hosts
0.5
ok
----
BEGIN{} 通常用于定义一些变量,例如BEGIN{FS=":";OFS="---"}
==awk命令格式:
awk 'pattern' filename 示例:awk -F: '/root/' /etc/passwd
awk '{action}' filename 示例:awk -F: '{print $1}' /etc/passwd
awk 'pattern {action}' filename 示例:awk -F: '/root/{print $1,$3}' /etc/passwd
示例:awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd
command |awk 'pattern {action}' 示例:df -P| grep '/' |awk '$4 > 25000 {print $4}'
awk工作原理
awk -F":" '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束
(2)然后,行被:分解成字段,每个字段存储在已编号的变量中,从$1开始
(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab
(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为$1,$3间有一个,逗号。逗号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格
(5)awk打印字段时,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。
awk中的特殊变量:
- NR: 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号
- NF: 表示字段数量, 当awk将行为记录时, 该变量相当于当前列号
- $0: 表示当前记录的文本内容
- $1: 表示当前记录的第一列文本内容
- $2: 表示当前记录的第二列文本内容
RS(输入记录分隔符) (record sign) The input record separator, by default a newline.
FS(输入字段分隔符) (filed sign)
NR(Number of record)行数
FNR按不同的文件飞开
ORS(输出记录分隔符) (output frecord sign)The output record separator, by default a newline.
OFS(输出字段分隔符) (output filed sign)
NF 字段个数 (Number of filed)
FS(输入字段分隔符) (filed sign)
FS(输入字段分隔符) (filed sign)
awk -F':' '{print $1}' pass
awk 'BEGIN{FS=":"} {print $1}' pass
OFS(输出字段分隔符) (output filed sign)
awk 'BEGIN{FS=":";OFS="===="} {print $1,$2}' pass
NR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号
awk '{print NR,$0}' pass
awk 'NR==3{print $0}' pass
FNR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号(不同文件分开)
awk '{print FNR,$0}' pass aa.txt
RS(输入记录分隔符)
awk 'BEGIN{RS=":"} {print NR,$0}' pass
ORS(输出记录分隔符)
awk 'BEGIN{RS=":";ORS="~~"} {print NR,$0}' pass
[root@host ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
OFS(输出字段分隔符) (output filed sign)
[root@host ~]# awk 'BEGIN{FS=":";OFS=".."} {print $1,$2}' /etc/passwd
root..x
bin..x
daemon..x
adm..x
lp..x
sync..x
shutdown..x
NR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号
[root@host ~]# awk -F: '{print NR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
6 isuo
7 IPADDR=192.168.6.5
8 hjahj123
9 GATEWAY=192.168.1.1
10 NETMASK=255.255.255.0
11 DNS=114.114.114.114
FNR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号(不同文件分开)
[root@host ~]# awk -F: '{print FNR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
1 isuo
2 IPADDR=192.168.6.5
3 hjahj123
4 GATEWAY=192.168.1.1
5 NETMASK=255.255.255.0
6 DNS=114.114.114.114
RS(输入记录分隔符)
[root@master ~]# cat passwd
root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# awk -F: 'BEGIN{RS="bash"} {print $0}' passwd
root:x:0:0:root:/root:/bin/
bin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin
ORS(输出记录分隔符)
[root@master ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' passwd
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
练习:将文件合并为一行
[root@master ~]# awk 'BEGIN{ORS="" } {print $0}' /etc/passwd
练习:把一行内容分为多行
[root@master ~]# cat d.txt
root:x:0:0:root:/root:/bin/bash
[root@master ~]# awk 'BEGIN{RS=":"} {print $0}' d.txt
root
x
0
0
root
/root
/bin/bash
awk实用理解案例:
1. 打印一个文件中的第2列和第3列
$ awk '{ print $2, $3}' filename
2. 打印指定行指定列的某个字符
$ awk -F: 'NR==3{ print $7 }' /etc/passwd
3. 统计一个文件的行数
$ awk '{ print NR}' filename
4. 在脚本中, 传递变量到awk中
$ var=1000
$ echo | awk -v VARIABLE=$var '{ print VARIABLE }'
5. 指定字段分隔符-F活在BEGIN{ FS=":" }
$ awk -F: '{ print $2, $3 }' filename
$ awk 'BEGIN{ FS=":" }{ print $2, $3 }' filename
6. 在awk中使用for循环
$ grep '05/Sep/2017' cd.mobiletrain.org.log | awk '{ ips[$1]++ } END {for (i in ips){print i,ips[i]}}' | sort -k2 -rn | head -n10
182.140.217.111 138
121.12.22.33 100
10.19.3.2 90
23.29.112.23 80
121.31.30.189 45
187.23.43.123 40
7. 在awk中使用if条件判断
$ awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
统计系统用户
$ awk -F":" '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd
脚本大全:
【脚本1】 批量建立用户
编写shell脚本,批量建立用户user_00, user_01, ... user_100并且所有用户同属于users组:
[root@master ~]# vim user.sh
#!/bin/bash
group=`cat /etc/group |grep -o users`
if [ $group == "users" ]
then
for i in `seq 0 100`
do
if [ $i -lt 10 ]
then
useradd -g users user_0$i
else
useradd -g users user_$i
fi
done
else
echo "users group not found!"
exit 1
fi
[root@master ~]# sh user.sh
[root@master ~]# ls /home/
Display all 102 possibilities? (y or n)
tom/ user_11/ user_24/ user_37/ user_50/ user_63/ user_76/ user_89/
user_00/ user_12/ user_25/ user_38/ user_51/ user_64/ user_77/ user_90/
user_01/ user_13/ user_26/ user_39/ user_52/ user_65/ user_78/ user_91/
user_02/ user_14/ user_27/ user_40/ user_53/ user_66/ user_79/ user_92/
user_03/ user_15/ user_28/ user_41/ user_54/ user_67/ user_80/ user_93/
user_04/ user_16/ user_29/ user_42/ user_55/ user_68/ user_81/ user_94/
user_05/ user_17/ user_30/ user_43/ user_56/ user_69/ user_82/ user_95/
user_06/ user_18/ user_31/ user_44/ user_57/ user_70/ user_83/ user_96/
user_07/ user_19/ user_32/ user_45/ user_58/ user_71/ user_84/ user_97/
user_08/ user_20/ user_33/ user_46/ user_59/ user_72/ user_85/ user_98/
user_09/ user_21/ user_34/ user_47/ user_60/ user_73/ user_86/ user_99/
user_10/ user_22/ user_35/ user_48/ user_61/ user_74/ user_87/
user_100/ user_23/ user_36/ user_49/ user_62/ user_75/ user_88/
删除以上脚本批量添加的用户:
[root@master ~]# vim user1.sh
#!/bin/bash
for i in `seq 0 100`
do
if [ $i -lt 10 ]
then
userdel -r user_0$i
else
userdel -r user_$i
fi
done
[root@master ~]# sh user1.sh
[root@master ~]# ls /home/
tom
后面的脚本就自己多动手实验吧:需要注意有些地方需要修改哦!!!!!!
【脚本2】每日生成一个文件
要求:请按照这样的日期格式(xxxx-xx-xx)每日生成一个文件,例如今天生成的文件为)2017-07-05.log, 并且把磁盘的使用情况写到到这个文件中,(不用考虑cron,仅仅写脚本即可)
[root@master ~]# vim file.sh
#!/bin/bash
fileName=`date +%F` #这里格式为输入时间年月日时分,f代表每日
c=`df -h`
echo "$c" > /root/$fileName.log
【脚本3】统计内存使用
写一个脚本计算一下linux系统所有进程占用内存大小的和。
实现代码:
[root@master ~]# vim free.sh
#!/bin/bash
count=0
# 这个循环会遍历出每个进程占用的内存大小
for i in `ps aux |awk '{print $6}' |grep -v 'RSS'`
do
# 将遍历出来的数字进行累加
count=$[$count+$i]
done
# 就得到所有进程占用内存大小的和了
echo "$count/kb"
[root@master ~]# sh free.sh
759524/kb
【脚本4】简单的监控脚本
设计一个简单的脚本,监控远程的一台机器(假设ip为192.168.229.61)的存活状态,当发现宕机时发一封邮件给你自己。
#!/bin/bash
ip="192.168.229.61"
email="user@example"
while 1
do
ping -c10 $ip > /dev/null 2>/dev/null
if [ $? != "0" ]
then
# 调用一个用于发邮件的脚本
python /usr/local/sbin/mail.py $email "$ip down" "$ip is down"
fi
sleep 30
done
【脚本5】简单的监控脚本
需求:
写一个脚本,判断本机的80端口(假如服务为httpd)是否开启着,如果开启着什么都不做,如果发现端口不存在,那么重启一下httpd服务,并发邮件通知你自己。脚本写好后,可以每一分钟执行一次,也可以写一个死循环的脚本,30s检测一次。
发邮件的脚本参考【脚本11】的示例代码。
代码:
#!/bin/bash
email="[email protected]"
if netstat -lntp |grep ':80' |grep 'httpd'
then
echo "80 port no problem"
exit
else
/usr/local/apache2.4/bin/apachectl restart
python mail.py $email "check_80port" "The 80 port is down."
n=`ps aux |grep httpd|grep -cv grep`
if [ $n -eq 0 ]
then
/usr/local/apache2/bin/apachectl start 2>/tmp/apache_start.err
fi
if [ -s /tmp/apache_start.err ]
then
python mail.py $mail 'apache_start_error' `cat /tmp/apache_start.err`
fi
fi
【脚本6】备份数据库
需求:
设计一个shell脚本来备份数据库,首先在本地服务器上保存一份数据,然后再远程拷贝一份,本地保存一周的数据,远程保存一个月。
假定,我们知道mysql root账号的密码,要备份的库为discuz,本地备份目录为/bak/mysql, 远程服务器ip为192.168.229.61,远程提供了一个rsync服务,备份的地址是 192.168.229.61::backup . 写完脚本后,需要加入到cron中,每天凌晨3点执行。
脚本代码:
#!/bin/bash
PATH=$PATHi:/usr/local/mysql/bin
week=`date +%w`
today=`date +d`
passwd="123456"
backdir="/data/mysql"
r_backupIP="192.168.123.30::backup"
exec 1>/var/log/mysqlbak.log 2>/var/log/mysqlbak.log
echo "mysql backup begin at `date +%F %T`."
# 本地备份
mysqldump -uroot -p$passwd --default-character-set=utf8 discuz >$backdir/$week.sql
# 同步备份到远程机器
rsync -az $backdir/$week.sql $r_backupIP/$today.sql
echo "mysql backup end at `date +%F %T`."
然后加入cron
0 3 * * * /bin/bash /usr/local/sbin/mysqlbak.sh
【脚本7】自动重启php-fpm服务
服务器上跑的是LNMP环境,近期总是有502现象。502为网站访问的状态码,200正常,502错误是nginx最为普通的错误状态码。由于502只是暂时的,并且只要一重启php-fpm服务则502消失,但不重启的话,则会一直持续很长时间。所以有必要写一个监控脚本,监控访问日志的状态码,一旦发生502,则自动重启一下php-fpm。
我们设定:
access_log /data/log/access.log
脚本死循环,每10s检测一次(假设每10s钟的日志条数为300左右)
重启php-fpm的方法是 /etc/init.d/php-fpm restart
脚本代码:
#!/bin/bash
access_log="/data/log/access.log"
N=10
while :
do
# 因为10秒大概产生300条日志记录
tail -n300 $access_log > /tmp/log
# 拿出log中包含502的日志行数
n_502=`grep -c "502" /tmp/log`
# 如果行数大于10
if [ $n_502 -ge $N ]
then
# 就记录一下系统状态
top -bn1 > /tmp/`date +%H%M%S`-top.log
vmstat 1 5 > /tmp/`date +%H%M%S`-vm.log
# 然后才重启服务,并把错误信息重定向
/etc/init.d/php-fpm restart 2> /dev/null
# 重启php-fpm服务后,应先暂缓1分钟,而后继续每隔10s检测一次
sleep(60)
fi
sleep(10)
done
【脚本8】监控mysql服务
假设,当前MySQL服务的root密码为123456,写脚本检测MySQL服务是否正常(比如,可以正常进入mysql执行show processlist),并检测一下当前的MySQL服务是主还是从,如果是从,请判断它的主从服务是否异常。如果是主,则不需要做什么。
实现代码:
#!/bin/bash
Mysql_c="mysql -uroot -p123456"
$Mysql_c -e "show processlist" >/tmp/mysql_pro.log 2>/tmp/mysql_log.err
n=`wc -l /tmp/mysql_log.err|awk '{print $1}'`
if [ $n -gt 0 ]
then
echo "mysql service sth wrong."
else
$Mysql_c -e "show slave status\G" >/tmp/mysql_s.log
n1=`wc -l /tmp/mysql_s.log|awk '{print $1}'`
if [ $n1 -gt 0 ]
then
y1=`grep 'Slave_IO_Running:' /tmp/mysql_s.log|awk -F : '{print $2}'|sed 's/ //g'`
y2=`grep 'Slave_SQL_Running:' /tmp/mysql_s.log|awk -F : '{print $2}'|sed 's/ //g'`
if [ $y1 == "Yes" ] && [ $y2 == "Yes" ]
then
echo "slave status good."
else
echo "slave down."
fi
fi
fi
【脚本9】抽签脚本
1、写一个脚本执行后,输入名字,产生随机数01-99之间的数字。
2、如果相同的名字重复输入,抓到的数字还是第一次抓取的结果,
3、前面已经抓到的数字,下次不能在出现相同数字。
4、第一个输入名字后,屏幕输出信息,并将名字和数字记录到文件里,程序不能退出
继续等待别的学生输入。
参考代码:
while :
do
read -p "Please input a name:" name
if [ -f /work/test/1.log ];then
bb=`cat /work/test/1.log | awk -F: '{print $1}' | grep "$name"`
if [ "$bb" != "$name" ];then #名字不重复情况下
aa=`echo $RANDOM | awk -F "" '{print $2 $3}'`
while :
do
dd=`cat /work/test/1.log | awk -F: '{print $2}' | grep "$aa"`
if [ "$aa" == "$dd" ];then #数字已经存在情况下
echo "数字已存在."
aa=`echo $RANDOM | awk -F "" '{print $2 $3}'`
else
break
fi
done
echo "$name:$aa" | tee -a /work/test/1.log
else
aa=`cat /work/test/1.log | grep "$name" | awk -F: '{print $2}'` #名字重复
echo $aa
echo "重复名字."
fi
else
aa=`echo $RANDOM | awk -F "" '{print $2 $3}'`
echo "$name:$aa" | tee -a /work/test/1.log
fi
done
【脚本10】检查服务
先判断是否安装http和mysql,没有安装进行安装,安装了检查是否启动服务,若没有启动则需要启动服务。
说明:操作系统为centos6,httpd和mysql全部为rpm包安装。
参考代码:
#!/bin/bash
if_install()
{
n=`rpm -qa|grep -cw "$1"`
if [ $n -eq 0 ]
then
echo "$1 not install."
yum install -y $1
else
echo "$1 installed."
fi
}
if_install httpd
if_install mysql-server
chk_ser()
{
p_n=`ps -C "$1" --no-heading |wc -l`
if [ $p_n -eq 0 ]
then
echo "$1 not start."
/etc/init.d/$1 start
else
echo "$1 started."
fi
}
chk_httpd
chk_mysqld
【脚本11】监控网卡
1.每10分钟检测一次指定网卡的流量
2.如果流量为0,则重启网卡
参考代码:
#!/bin/bash
LANG=en
n1=`sar -n DEV 1 60 |grep eth0 |grep -i average|awk '{print $5}'|sed 's/\.//g'`
n2=`sar -n DEV 1 60 |grep eth0 |grep -i average|awk '{print $6}'|sed 's/\.//g'`
if [ $n1 == "000" ] && [ $n2 == "000" ]
then
ifdown eth0
ifup eth0
fi
【脚本12】杀死进程
把当前用户下所有进程名字中含有”java”的进程关闭。
ps -u $USER |awk '$NF ~ /java/ {print $1}'|xargs kill
【脚本13】监控节点
一个网站,使用了cdn,全国各地有几十个节点。需要你写一个shell脚本来监控各个节点是否正常。
假如:
监控的url为www.xxx.com/index.php
源站ip为88.88.88.88
参考代码:
#!/bin/bash
url="www.xxx.com/index.php"
s_ip="88.88.88.88"
curl -x $s_ip:80 $url > /tmp/source.html 2>/dev/null
for ip in `cat /tmp/ip.txt`
do
curl -x $ip:80 $url 2>/dev/null >/tmp/$ip.html
[ -f /tmp/$ip.diff ] && rm -f /tmp/$ip.diff
touch /tmp/$ip.diff
diff /tmp/source.html /tmp/$ip.html > /tmp/$ip.diff 2>/dev/null
n=`wc -l /tmp/$ip.diff|awk '{print $1}'`
if [ $n -lt 0 ]
then
echo "node $ip sth wrong."
fi
done
【脚本14】监控cpu使用率
用shell写一个监控服务器cpu使用率的监控脚本。
思路:用top -bn1 命令,取当前空闲cpu百份比值(只取整数部分),然后用100去剑这个数值。
参考代码:
#!/bin/bash
while :
do
idle=`top -bn1 |sed -n '3p' |awk '{print $5}'|cut -d . -f1`
use=$[100-$idle]
if [ $use -gt 90 ]
then
echo "cpu use percent too high."
#发邮件省略
fi
sleep 10
done
【脚本15】自动增加公钥
写一个shell脚本,当我们执行时,提示要输入对方的ip和root密码,然后可以自动把本机的公钥增加到对方机器上,从而实现密钥认证。
参考代码:
#!/bin/bash
read -p "Input IP: " ip
ping $ip -w 2 -c 2 >> /dev/null
## 查看ip是否可用
while [ $? -ne 0 ]
do
read -p "your ip may not useable, Please Input your IP: " ip
ping $ip -w 2 -c 2 >> /dev/null
done
read -p "Input root\'s password of this host: " password
## 检查命令子函数
check_ok() {
if [ $? != 0 ]
then
echo "Error!."
exit 1
fi
}
## yum需要用到的包
myyum() {
if ! rpm -qa |grep -q "$1"
then
yum install -y $1
check_ok
else
echo $1 already installed
fi
}
for p in openssh-clients openssh expect
do
myyum $p
done
## 在主机A上创建密钥对
if [ ! -f ~/.ssh/id_rsa ] || [ ! -f ~/.ssh/id_rsa.pub ]
then
if [ -d ~/.ssh ]
then
mv ~/.ssh/ ~/.ssh_old
fi
echo -e "\n" | ssh-keygen -t rsa -P ''
check_ok
fi
## 传私钥给主机B
if [ ! -d /usr/local/sbin/rsync_keys ]
then
mkdir /usr/local/sbin/rsync_keys
fi
cd /usr/local/sbin/rsync_keys
if [ -f rsync.expect ]
then
d=`date +%F-%T`
mv rsync.expect $d.expect
fi
#创建远程同步的expect文件
cat > rsync.expect <<EOF
#!/usr/bin/expect
set host [lindex \$argv 0]
#主机B的密码
set passwd [lindex \$argv 1]
spawn rsync -av /root/.ssh/id_rsa.pub root@\$host:/tmp/tmp.txt
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "\$passwd\r" }
}
expect eof
spawn ssh root@\$host
expect {
"password:" { send "\$passwd\r" }
}
expect "]*"
send "\[ -f /root/.ssh/authorized_keys \] && cat /tmp/tmp.txt >>/root/.ssh/authorized_keys \r"
expect "]*"
send "\[ -f /root/.ssh/authorized_keys \] || mkdir -p /root/.ssh/ \r"
send "\[ -f /root/.ssh/authorized_keys \] || mv /tmp/tmp.txt /root/.ssh/authorized_keys\r"
expect "]*"
send "chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys\r"
expect "]*"
send "exit\r"
EOF
check_ok
/usr/bin/expect /usr/local/sbin/rsync_keys/rsync.expect $ip $password
echo "OK,this script is successful. ssh $ip to test it"
shell运维脚本实战
当各位读者掌握了前面的内容,那么久尝试一下下面的实战技巧技术吧!!!!!下面内容具有一定难度
• 实战项目1: sed实现网络配置
• 实战项目2: sed实现sshd配置
• 实战项目3: sed实现nginx配置
• 实战项目4: 关闭本机SELinux的功能
• 实战项目5: 在/etc/sudoers配置文件中添加内容
• 实战项目6: 将固定文件的内容添加到nginx配置文件
• 实战项目7: zabbix_agentd.conf配置文件修改
• 实战项目8: awk统计/etc/password各种shell数量
• 实战项目9: awk统计网站访问各种状态数量
• 实战项目10: awk统计当前访问的每个IP的数量
• 实战项目11: 统计Nginx日志中某一天的PV量
• 实战项目12: 获取获得内存使用情况
• 实战项目13: 基于时间戳的备份程序
• 实战项目14: 批量主机软件部署程序
• 实战项目15: Web日志访问量分析程序
• 实战项目16: 编写系统初始化脚本
• 实战项目17: LAMP终级部署
• 实战项目18: Linux系统状态收集及分析
• 实战项目19: Web访问日志全文分析