shell基础
shell脚本的目的:将复杂重复的执行过程,通过逻辑代码组成一个脚本文件,只需要执行脚本文件即可
脚本中的变量被引用时,会被赋值,脚本执行完后会根据执行脚本的方式决定变量赋值是否保留
执行脚本的方式:
source 脚本名
. 脚本名
#以上两种方式执行脚本时,被赋值的变量会保留在当前shell
bash 脚本名
./脚本名
#以上方式执行脚本时,变量会在shell执行完脚本后消失
数值计算命令
双小括号
双小括号"(())"会运算里面的数值,并返回运算结果
[root@test ~]# echo $((10*2))
20
[root@test ~]# ((b=10*2))
[root@test ~]# echo $b
20
[root@test ~]#
let命令
let命令用于数值计算,效果等同于双小括号"(())",但是双小括号的效率更高
[root@test ~]# a=10
[root@test ~]# let a=a+10 && echo $a
20
[root@test ~]#
expr命令
expr命令可以用于数值计算、逻辑判断等
#查看expr命令的帮助文档
[root@test ~]# expr --help
Usage: expr EXPRESSION
or: expr OPTION
--help display this help and exit
--version output version information and exit
.........
or available locally via: info '(coreutils) expr invocation'
[root@test ~]#
#expr用于数值计算,需要传入参数进行计算,每个参数以空格隔开
[root@test ~]# expr 10 - 5
5
#但是使用有特殊含义的元字符时(例如*、/),会识别失败,需要加上反斜杠转义
[root@test ~]# expr 10 * 5
expr: syntax error: unexpected argument ‘1.txt’
[root@test ~]# expr 10 \* 5
50
#通过length选项可以统计字符长度
[root@test ~]# expr length abcde
5
#除了数值计算、字符长度统计,还可以进行逻辑判断,0为假,1为真
[root@test ~]# expr 1 \> 10
0
[root@test ~]# expr 1 \< 10
1
expr模式匹配
#expr支持模式匹配功能,有两个特殊符号,":"和".*"
#":"表示计算字符的长度
#".*"表示匹配任意长度的字符,这个可以自定义,安装需求匹配
#用法:expr [字符串] ":" [匹配要求]
[root@test ~]# expr abcde,abcde,abcde ":" ".*"
17
#根据文件的后缀来统计文件名的长度
[root@test ~]# expr test.sh ":" ".*\.sh"
7
expr检查文件名是否合法
[root@test ~]# cat test1.sh
#! /bin/bash
if expr $1 ":" ".*\.sh" &> /dev/null
then
echo "this is a script file"
else
echo "this is not a script file"
fi
[root@test ~]# chmod +x test1.sh
[root@test ~]# ./test1.sh abcd.sh
this is a script file
[root@test ~]# ./test1.sh abcd.dasda
this is not a script file
[root@test ~]#
expr找出长度不大于6的单词
[root@test ~]# cat test1.sh
#! /bin/bash
for a in a ab abc abcd abcde abcdef abcdefg
do
if [ `expr length $a ` -le 5 ];then
echo $a
fi
done
[root@test ~]# chmod +x test1.sh
[root@test ~]# ./test1.sh
a
ab
abc
abcd
abcde
[root@test ~]#
bc命令
bc命令是一个在命令行中运行的计算器,支持交互式,并且支持小数
#直接输入bc命令可以进入交互式界面,如果没有bc命令就yum安装
[root@test ~]# bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
10 - 5
5
2*10
20
~~~
bc命令结合管道符计算
~~~bash
#echo不识别乘号,加上管道符和bc命令就可以识别出来
[root@test ~]# echo "2*4"
2*4
[root@test ~]# echo "2*4" | bc
8
[root@test ~]#
~~~
计算1到1000的总和
~~~bash
#方法一
[root@test ~]# echo {1..1000} | tr " " "+" | bc
500500
[root@test ~]#
#方法二
#seq生成序列,-s选项指定分隔符
[root@test ~]# seq -s "+" 1000 | bc
500500
[root@test ~]#
#方法三
#expr接受参数,所有在seq -s替换时主动加上空格,再传给expr进行计算
[root@test ~]# seq -s " + " 1000 |xargs expr
500500
[root@test ~]#
~~~
### awk命令进行数值计算
awk一般用于处理文本,也可以用于数值计算
~~~bash
[root@test ~]# echo "10 20" | awk '{print $1*$2}'
200
[root@test ~]#
~~~
### 中括号进行数值计算
~~~bash
[root@test ~]# num=$[5*2] && echo $num
10
[root@test ~]#
~~~
## 数值比较
### 数值比较的符号
![在这里插入图片描述](https://img-blog.csdnimg.cn/c25d5331fabb4805822ed1d85308c94d.png#pic_center)
注意!使用数学符号时需要加上反斜杠“\”转义,不然无法识别
双中括号与单中括号的用法、效果一致,但是双中括号**支持正则表达式**,而且**不需要转义**,一般使用的是单中括号,但是在复杂的脚本中会使用到双中括号。
## 逻辑判断
### 逻辑判断符号
![在这里插入图片描述](https://img-blog.csdnimg.cn/6bf868765dc043559cac742d75795635.png#pic_center)
## 编写脚本常用命令
### read
~~~bash
#一般用于接收用户输入的参数
#-p 设置提示信息
#-t 设置过期时间
[root@test ~]# read -p "请输入姓名和年龄:" -t 20 name age
请输入姓名和年龄:tom 18
[root@test ~]# echo $name $age
tom 18
[root@test ~]#
~~~
### test
~~~bash
#test命令用于条件测试,判断表达式结果的真假,真为0,假为非0,通过“$?”取值
#-e 判断该文件(普通文件)是否存在,存在为真,不存在为假
[root@test ~]# ls
anaconda-ks.cfg a.txt
[root@test ~]# test -e a.txt
[root@test ~]# echo $?
0
[root@test ~]# test -e b.txt
[root@test ~]# echo $?
1
[root@test ~]#
#-f 只判断文件是否存在
#-d 只判断目录是否存在
#test简单用法:检查目录是否存在,不存在就创建。
[root@test ~]# cd /opt/
[root@test opt]# ls
data
[root@test opt]# test -d "sakura" && echo "该目录已经存在" || mkdir sakura
[root@test opt]# ls
data sakura
[root@test opt]# test -d "sakura" && echo "该目录已经存在" || mkdir sakura
该目录已经存在
[root@test opt]#
~~~
### 中括号[ ]
中括号可以用来条件测试,效果与test命令相同
~~~bash
[root@test opt]# ls
data sakura
[root@test opt]# [ -d sakura ] && echo "该目录存在" || echo "该目录不存在"
该目录存在
#如果使用变量,需要加上双引号
[root@test opt]# file=sakura123
[root@test opt]# [ -d "$sakura123" ] && echo "该目录存在" || echo "该目录不存在"
该目录不存在
[root@test opt]#
~~~
## 函数
在脚本中定义的函数可以在后面的代码中调用,可以将一些需要多次执行、重复的操作写成一个函数,需要的时候直接调用即可。
~~~bash
#标准shell函数写法
function 函数名(){
函数主体
return 返回值
}
#偷懒shell函数写法
function 函数名{
函数主体
return 返回值
}
#简洁shell函数写法
函数名(){
函数主体
return 返回值
}
#需要调用时,输入函数名即可调用。例:
[root@test ~]# cat a.txt
#! /bin/bash
printf_a(){
a=10
echo $a
}
printf_a
[root@test ~]# ./a.txt
10
[root@test ~]#
#注意,exit和return都是返回状态值,但是return只能在脚本中使用,表示结束当前函数,exit是结束当前的shell。
#如果将函数写入到一个文件中,再进行调用,需要使用source读取
~~~
## if条件语句
if语句在脚本中比较常见,在命令行中也可以直接使用,以分号结尾
~~~bash
#语法:(单分支)
if [条件表达式];then
[代码]
if
#if后面跟判断条件表达式,以“;then”结束
#语法:(双分支)
if [条件表达式];then
[代码]
if [条件表达式];then
[代码]
if
fi
#语法:(if--eles)
if [条件表达式];then
[代码]
else
[代码]
fi
#语法:(多分支)
if [条件表达式];then
[代码]
elif [条件表达式];then
[代码]
else
[代码]
fi
~~~
### if实践
**if单分支**
~~~bash
#if中的条件判断语句可以使用单中括号,双中括号和test命令
#判断文件是否存在,存在返回yes
[root@test ~]# touch sakura.txt
[root@test ~]# vim test1.sh
[root@test ~]# cat test1.sh
#! /bin/bash
if [ -f ./sakura.txt ]; then
echo ":yes []"
fi
if [[ -f ./sakura.txt ]]; then
echo ":yes [[]]"
fi
if test -f ./sakura.txt; then
echo ":yes test"
fi
[root@test ~]# bash test1.sh
:yes []
:yes [[]]
:yes test
[root@test ~]# rm -rf sakura.txt
[root@test ~]# bash test1.sh
[root@test ~]#
#模拟监控内存并发送告警邮件
[root@test ~]# vim test1.sh
[root@test ~]# cat test1.sh
#! /bin/bash
free_size=`free -m | awk 'NR==2 {print $NF}'`
mail_content="free size deficiency"
if [ "$free_size" -lt "10000" ];then
#本机还未配置邮件服务,此处发送邮件命令供参考
#语法:mail -s "邮件主题" 收件人地址 < 写有发送内容的文件
#echo $mail_content | tee /tmp/mail_content.txt
#mail -s "free size deficiency" 123456@qq.com < /tmp/mail_content.txt
#这里模拟效果,就直接输出提示
echo "free size deficiency!!!"
fi
[root@test ~]# bash test1.sh
free size deficiency!!!
#加入计划任务
[root@test ~]# crontab -e
no crontab for root - using an empty one
crontab: installing new crontab
[root@test ~]# crontab -l
*/3 * * * * bash ./test1.sh &> /dev/null
[root@test ~]# systemctl restart crond.service
~~~
**if多分支**
~~~bash
#在进行多次判断时,相比于单分支,多分支if更加简洁
#例:输入两个数进行大小比较
~~~
## case条件语句
case一般用于多分支判断,并且判断条件较简单时
![在这里插入图片描述](https://img-blog.csdnimg.cn/62ad2956d5aa4be8ba52b3d9283bd452.png#pic_center)
~~~bash
#语法:
case 变量 in
"条件1")
"代码1"
;;
"条件2")
"代码2"
;;
.......
"条件n")
"代码n"
;;
esac
~~~
## for循环
~~~bash
#语法
for 循环变量 in 循环范围
do
"代码"
done
~~~
# shell开发实战
**用户输入内容,判断输入的是否符合要求**
~~~bash
#只能输入1和2,输入其他的内容输出提示信息
[root@test ~]# cat test1.sh
#! /bin/bash
read -p "请输入数字1或2:" num
[ "$num" -eq "1" -o "$num" -eq "2" ] && {
echo "$num"
exit 0
}
[ "$num" -ne "1" -a "$num" -ne "2" ] && {
echo "请输入1或2"
exit 1
}
#由用户输入选项来选择安装lnmp或lamp的web架构(这里的lanp和lnmp并不是实际的安装脚本,此处只是用来代替选项)
[root@test ~]# mkdir scripts_file
[root@test ~]# cd scripts_file/
[root@test scripts_file]# touch lamp.sh lnmp.sh
[root@test scripts_file]# ls
lamp.sh lnmp.sh
[root@test scripts_file]# echo "echo lamp install script" > lamp.sh
[root@test scripts_file]# echo "echo lnmp install script" > lnmp.sh
[root@test scripts_file]# chmod +x lamp.sh lnmp.sh
[root@test scripts_file]# ll
total 8
-rwxr-xr-x. 1 root root 25 Mar 31 13:43 lamp.sh
-rwxr-xr-x. 1 root root 25 Mar 31 13:44 lnmp.sh
[root@test scripts_file]#
~~~
选择安装lamp或lnmp,这里只是实现选择执行哪个脚本,具体的安装lamp、lnmp的脚本在后文
~~~bash
[root@test scripts_file]# vim install_lamp_or_lnmp.sh
[root@test scripts_file]# cat install_lamp_or_lnmp.sh
#!/bin/bash
install_path=./script_files
echo "请选择需要执行的操作:"
echo "1、install LAMP"
echo "2、install LNMP"
echo "3、exit"
read -p "请输入:" num
#判断输入的是否是数字,使用expr命令对变量num进行处理,如果处理的不是数字,那么返回值就会显示非零,根据返回值来判断输入的是否是数字
expr $num+1 > /dev/null
[ `echo $?` -ne "0" -o "$num" -gt "3" ] && {
echo "输入内容有误,请重新执行脚本并输入正确的选项"
}
[ "$num" -eq "3" ] && {
exit
}
#####################################################
#当输入1时,调用LAMP的安装脚本
[ "$num" -eq "1" ] && {
echo "正在执行安装LAMP脚本,请稍等。。。。"
sleep 2;
#判断脚本是否有执行权限,有就继续执行,没有就提示并退出
[ -x "$install_path"/LAMP.sh ] || {
echo "脚本不存在或没有执行权限,请切换root用户并确认有执行权限后再重试"
exit 1
}
#执行LAMP安装脚本
source ${install_path}/LAMP.sh
exit $?
}
######################################################
#当输入2时,调用LNMP脚本
[ "$num" -eq "2" ] && {
echo "正在执行安装LNMP脚本,请稍等。。。。"
sleep 2;
#判断脚本是否有执行权限
[ -x "$install_path"/LNMP.sh ] || {
echo "脚本不存在或没有执行权限,请切换root用户并确认有执行权限后再重试"
exit 1
}
#执行LNMP安装脚本
source ${install_path}/LNMP.sh
exit $?
}
~~~
**MySQL服务监控**
![在这里插入图片描述](https://img-blog.csdnimg.cn/face7a97adfc497aae14401d7714203c.png#pic_center)
~~~bash
#在服务器本地监控MySQL端口
#通过netstat或ss命令过滤出MySQL的端口号,通过端口监控得知MySQL状态
[root@test ~]# netstat -tunlp | grep mysql
tcp6 0 0 :::3306 :::* LISTEN 3333/mysqld
#统计行数,非零表示MySQL存活
[root@test ~]# netstat -tunlp | grep mysql | wc -l
1
#远程监控MySQL端口
#命令需要安装。(nmap:端口扫描);(telnet:);(nc:)
[root@test ~]# yum -y install telnet nmap nc
#nmap [ip地址] -p [端口]
[root@test ~]# nmap 127.0.0.1 -p 3306
Starting Nmap 7.70 ( https://nmap.org ) at 2023-04-03 12:04 CST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000062s latency). #host存活
PORT STATE SERVICE
3306/tcp open mysql #MySQL3306端口是打开状态
Nmap done: 1 IP address (1 host up) scanned in 0.26 seconds
#进行过滤
[root@test ~]# nmap 127.0.0.1 -p 3306 | grep open | wc -l
1
~~~
通过访问应用程序接口,读取数据库,查看是否可以读取到数据,从而判断MySQL是否运行
~~~bash
#通过php
#卸载php的旧依赖,安装新依赖
[root@test ~]# yum remove php-mysql
No match for argument: php-mysql
No packages marked for removal.
Dependencies resolved.
Nothing to do.
Complete!
[root@test ~]# yum -y install php-mysqlnd php
#编写php代码
[root@test ~]# vim mysql_test.php
[root@test ~]# cat mysql_test.php
<?php
$mysql_id=mysqli_connect("localhost","root","sakura123!") or mysql_error();
if ($mysql_id){
echo "mysql success!!!!!!!";
}else{
echo mysql_error();
}
[root@test ~]# php mysql_test.php
mysql success!!!!!!![root@test ~]#
#通过python
#安装python3开发环境依赖
[root@test ~]# yum -y install python3 python3-devel python3-pip
[root@test ~]# pip3 install pymysql
#编写python连接MySQL代码
[root@test ~]# vim test_python.py
[root@test ~]# python3 test_python.py
数据库连接正确,版本是:10.3.28-MariaDB
[root@test ~]# vim test_python.py
[root@test ~]# cat test_python.py
import pymysql
db = pymysql.connect(
host="localhost",
user='root',
port=3306,
password='sakura123!',
db='mysql',
charset='utf8'
)
cursor=db.cursor()
cursor.execute('select version()')
data=cursor.fetchone()
print("数据库连接正确,版本是:%s"%data)
db.close()
[root@test ~]# python3 test_python.py
数据库连接正确,版本是:10.3.28-MariaDB
[root@test ~]#
~~~
**编写脚本监控MySQL**
~~~bash
[root@test ~]# cat test1.sh
#! /bin/bash
###方法一:
if [ `netstat -tunlp | grep mysql | wc -l` -eq "1" ];then
echo "数据库服务正常运行"
else
echo "数据库服务停止"
systemctl restart mariadb
fi
###方法二:
if [ `ss -tunlp | grep mysql | wc -l` -eq "1" ];then
echo "数据库服务正常运行"
else
echo "数据库服务停止"
systemctl restart mariadb
fi
###方法三:
php ./mysql_test.php
if [ "$?" -eq "0" ];then
echo "数据库服务正常运行"
else
echo "数据库服务停止"
systemctl restart mariadb
fi
###方法四:
python3 test_python.py
if [ "$?" -eq "0" ];then
echo "数据库服务正常运行"
else
echo "数据库服务停止"
systemctl restart mariadb
fi
[root@test ~]# bash test1.sh
数据库服务正常运行
数据库服务正常运行
mysql success!!!!!!!数据库服务正常运行
数据库连接正确,版本是:10.3.28-MariaDB
数据库服务正常运行
[root@test ~]#
~~~
**开发rsync启停脚本**
~~~bash
#安装rsync服务,并启动
[root@test ~]# yum -y install rsync*
[root@test ~]# /usr/bin/rsync --daemon
[root@test ~]# ss -anltup | grep 873
tcp LISTEN 0 5 0.0.0.0:873 0.0.0.0:* users:(("rsync",pid=12933,fd=4))
tcp LISTEN 0 5 [::]:873 [::]:* users:(("rsync",pid=12933,fd=5))
#编写rsync的启动脚本
[root@test ~]# cat rsync.sh
#! /bin/bash
#判断用户输入的参数个数是否为1个,如果不符合要求,提示用户重新输入正确参数
if [ "$#" -ne "1" ];then
echo "Usage: $0 {start|stop|restart}"
exit 1
fi
#当选择启动rsync时
if [ "$1" == "start" ];then
/usr/bin/rsync --daemon
sleep 2
#验证端口是否存活
if [ `ss -anltup | grep rsync | wc -l` -eq "0" ];then
echo "服务端口未启动!!!"
exit 1
else
echo "服务端口已启动"
exit 0
fi
elif [ "$1" == "stop" ];then
killall rsync &> /dev/null
sleep 2
if [ `ss -anltup | grep rsync | wc -l` -ne "0" ];then
echo "服务未关闭,端口还存在!!!"
exit 1
else
echo "服务端口已正常关闭"
exit 0
fi
elif [ "$1" == "restart" ];then
killall rsync &> /dev/null
sleep 1
stop_result=`ss -anltup | grep rsync | wc -l`
/usr/bin/rsync --daemon
sleep 1
start_result=`ss -anltup | grep rsync | wc -l`
if [ "$stop_result" -eq "0" -a "$start_result" -ne "0" ];then
echo "服务重启成功"
exit 0
else
echo "服务重启失败"
fi
else
echo "请输入正确的参数!"
echo "Usage: $0 {start|stop|restart}"
fi
[root@test ~]#
~~~
**监测网站是否存活**
~~~bash
[root@test ~]# cat ./scripts_file/check_web_survival.sh
#!/bib/bash
if [ "$#" -ne "1" ];then
echo "提示,请输入正确的参数格式"
echo "Usage:$0 web_url "
exit 1
fi
yum -y install wget &> /dev/null && \
wget --spider -q -o /dev/null --tries=1 -T 5 $1
if [ "$?" -eq "0" ];then
echo "$1网站存活,正常运行中。。。。。"
else
echo "$1网站无法访问,请检查网站服务!!!!"
fi
[root@test ~]#
~~~