XShell编程
本来小黄在学习Linux的路上是想跳过Shell编程的,后来听老韩讲对于Java程序员来说,可能在工作的时候,会要求编写一个shell脚本来对服务器进行维护,比如定时备份数据库的脚本。那还是得了解一下,至少简单的会写会看!!!
什么是Shell?
shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统及程序,用户可以用shell来启动、挂起、停止甚至是编写一些程序。
就像我们平时利用xshell、finalshell等工具,执行的语句会通过命令行解释器交给Linux内核来处理。
helloworld
所有新的语言学习的过程中,第一项永远都是向世界呐喊,我们学习Shell语言也不例外
创建文件
[root@yellowstarEdu01 ~]# vim hello.sh
编写脚本,shell语言要以#!/bin/bash
开头
#!/bin/bash
echo "hello,world!" # echo是输出,类似于sout
直接使用vim创建的文件是没有执行权限的,可以用以下两种方式解决
# 方案一:使用sh命令
[root@yellowstarEdu01 ~]# sh hello.sh
hello,world!
# 方案二:添加执行权限
[root@yellowstarEdu01 ~]# chmod u+x hello.sh
-rwxr--r--. 1 root root 32 2月 8 09:08 hello.sh
[root@yellowstarEdu01 ~]# ./hello.sh
hello,world!
shell变量
基础变量
Linux中Shell变量分为系统变量和用户自定义变量
系统变量如: H O M E , HOME, HOME,PWD等
[root@yellowstarEdu01 shcode]# echo $PWD
/root/shcode
这里我们重点学习的是用户自定义变量。
基本语法
- 定义变量:变量名=值(注意等号两边不能有空格)
- 撤销变量:unset 变量名
- 声明静态:readonly 变量名=值
测试
#!/bin/bash
# 定义变量A
A=100
echo A=$A
# 撤销变量A
unset A
echo A=$A
# 声明静态变量B
readonly B=200
echo B=$B
# 静态变量不能unset
unset B
[root@yellowstarEdu01 shcode]# ./test.sh
A=100
A=
B=200
./test.sh: 第 12 行:unset: B: 无法反设定: 只读 variable
定义变量的规则
- 变量名由字母、数字、下划线组成,但是不能以数字开头
- 等号两侧不能有空格
- 变量名建议大写
将命令返回值赋给变量
C=`date` #反引号,运行里面的命令,并将结果返回给C
D=$(date) # $()等价于反引号
echo C=$C
echo D=$D
[root@yellowstarEdu01 shcode]# ./test.sh
C=2022年 02月 08日 星期二 10:53:52 CST
D=2022年 02月 08日 星期二 10:53:52 CST
设置环境变量
环境变量,我们可以当作全局变量来使用,可以作用于所有的shell脚本中
基本语法
- export 变量名=变量值 (将shell变量输出为环境变量)
- source 配置文件 (让修改后的配置信息立即生效)
- echo $变量名 (查询环境变量的值)
测试
-
在/etc/profile文件中定义TOMCAT_HOME环境变量
[root@yellowstarEdu01 shcode]# vim /etc/profile # 在最后添加以下语句 export TOMCAT_HOME=/opt/tomcat
-
查看环境变量TOMCAT_HOME的值
# 直接查询不显示结果,需要先source [root@yellowstarEdu01 shcode]# echo $TOMCAT_HOME [root@yellowstarEdu01 shcode]# source /etc/profile [root@yellowstarEdu01 shcode]# echo $TOMCAT_HOME /opt/tomcat
-
在另一个shell程序中使用TOMCAT_HOME
#!/bin/bash A=$TOMCAT_HOME echo A=$A [root@yellowstarEdu01 shcode]# ./tomcat.sh A=/opt/tomcat
位置参数变量
当我们执行一个shell脚本时,如果希望获取到命令行的参数信息,就可以使用到位置参数
比如 :./myshell.sh 100 200,这个就是一个执行shell的命令行
基本语法
- $n:n为数字,$0代表命令本身,$1- 9 代 表 第 一 到 第 九 个 参 数 , 十 以 上 的 参 数 用 大 括 号 , 9代表第一到第九个参数,十以上的参数用大括号, 9代表第一到第九个参数,十以上的参数用大括号,{10}
- $*:代表命令行中所有的参数,把所有的参数看成一个整体
- $@:代表命令行中所有的参数,把每个参数区分对待
- $#:命令行中所有参数的个数
测试
#!/bin/bash
echo 0=$0 1=$1 2=$2
echo 所有的参数=$*
echo $@
echo 参数的个数为=$#
[root@yellowstarEdu01 shcode]# ./position.sh 100 200
0=./position.sh 1=100 2=200
所有的参数=100 200
100 200
参数的个数为=2
预定义变量
shell设计者事先已经定义好的变量,可以直接在shell脚本中使用
基本语法
- $$:当前进程的进程ID
- $!:后台运行的最后一个进程的ID
- $?:最后一次执行的命令的返回状态,值为0,证明上一个命令正确执行
测试
#!/bin/bash
echo 当前进程的PID=$$
# 以后台方式运行一个脚本
/root/shcode/tomcat.sh &
echo 最后一个脚本的PID=$!
echo 执行的脚本返回值=$?
[root@yellowstarEdu01 shcode]# ./preVar.sh
当前进程的PID=114724
最后一个脚本的PID=114725
执行的脚本返回值=0
[root@yellowstarEdu01 shcode]# A=/opt/tomcat
运算以及流程控制
运算符
基本语法
- ( ( 运 算 式 ) ) 或 ((运算式)) 或 ((运算式))或[运算式] 或 expr m + n
- 注意expr运算符之间要有空格
- expr
\*
,/
,%
代表乘,除,取余
测试
#!/bin/bash
# 计算(2+3)*4的值
expr $[2+3] \* 4
# 命令行输入两个整数,求和
echo $[$1+$2]
[root@yellowstarEdu01 shcode]# ./sum.sh 100 20
20
120
条件判断
常用判断条件
- =:字符串比较
- 整数比较
- -lt:小于
- -le:小于等于
- -eq:等于
- -gt:大于
- -ge:大于等于
- -ne:不等于
- 按照文件权限进行判断
- -r:有读的权限
- -w:有写的权限
- -x:有执行的权限
- 按照文件类型进行判断
- -f:文件存在并且是一个文件
- -e:文件存在
- -d:文件存在并是一个目录
基本语法
[ condition ] 注意:condition前后必须要有空格
condition非空返回 true,空则返回false
if [ 条件判断式 ]
then
代码
fi #if语句结束
测试
#!/bin/bash
# 判断字符串ok是否等于ok
if [ "ok"="ok" ]
then
echo yes
fi
# 判断23是否大于等于22
if [ 23 -ge 22 ]
then
echo 大于
fi
# /root/shcode/aaa.txt文件是否存在
if [ -f /root/shcode/aaa.txt ]
then
echo 文件存在
fi
[root@yellowstarEdu01 shcode]# ./ifdemo.sh
yes
大于
# 因为文件不存在,所以没有输出
多分支
相当于if…else…
if [ 条件判断式 ]
then
代码
elif [ 条件判断式 ]
then
代码
fi #if语句结束
测试
#!/bin/bash
# 输入参数,大于等于60输出及格,反之输出不及格
if [ $1 -ge 60 ]
then
echo 及格
elif [ $1 -lt 60 ]
then
echo 不及格
fi
[root@yellowstarEdu01 shcode]# ./grade.sh 50
不及格
[root@yellowstarEdu01 shcode]# ./grade.sh 80
及格
case语句
类似于Java中的switch…case…
基本语法
case $变量名 in
"值1" )
满足值1执行代码
;;
"值2" )
满足值2执行代码
;;
* )
都不满足,执行代码
;;
esac
测试
#!/bin/bash
# 当命令行参数为1时,输出周一,2时,输出周二,其他情况输出其他
case $1 in
"1" )
echo 周一
;;
"2" )
echo 周二
;;
* )
echo 其他
;;
esac
[root@yellowstarEdu01 shcode]# ./casedemo.sh 1
周一
[root@yellowstarEdu01 shcode]# ./casedemo.sh 3
其他
[root@yellowstarEdu01 shcode]# ./casedemo.sh 2
周二
for循环
基本语法1
for 变量 in 值1 值2 值3...
do
程序
done
测试
#!/bin/bash
# 打印命令行输入的参数,这里可以看出$*和$@的区别
for i in "$*"
do
echo 参数为:$i
done
echo ===========================
for i in "$@"
do
echo 参数为:$i
done
# 输出,从这里我们可以看出$*是把所有参数看成一个整体
[root@yellowstarEdu01 shcode]# ./testfor1.sh 100 200 300
参数为:100 200 300
===========================
参数为:100
参数为:200
参数为:300
基础语法2
for(( 初始值;循环控制条件;变量变化 )) #支持++,--写法
do
程序
done
测试
#!/bin/bash
# 输出从1加到100的值
SUM=0
for (( i=0;i<=100;i++ ))
do
SUM=$[$SUM+$i]
done
echo 结果为:$SUM
[root@yellowstarEdu01 shcode]# ./testfor2.sh
结果为:5050
while循环
基本语法
while [ 条件判断式 ]
do
程序
done
测试
#!/bin/bash
i=0
SUM=0
while [ $i -le $1 ]
do
SUM=$[$SUM+$i]
i=$[$i+1]
done
echo 结果为:$SUM
#输出
[root@yellowstarEdu01 shcode]# ./testWhile.sh 10
结果为:55
[root@yellowstarEdu01 shcode]# ./testWhile.sh 100
结果为:5050
read读取控制台输入
基本语法
read (选项)(参数)
-p:读取值时的提示符
-t:指定读取时等待的时间(秒),如果没有在指定时间内输入,就不再等待
测试
#!/bin/bash
# 读取控制台输入的一个num1值
read -p "请输入一个数NUM1:" NUM1
echo 控制台输入的值为:$NUM1
# 读取控制台输入的一个num2值,在5秒内输入
read -t 5 -p "请输入一个数NUM2:" NUM2
echo 控制台输入的值为:$NUM2
# 输出,第二个参数我们不输入,5秒后终止输入
[root@yellowstarEdu01 shcode]# ./testread.sh
请输入一个数NUM1:10
控制台输入的值为:10
请输入一个数NUM2:控制台输入的值为:
函数
shell编程和其他编程语言一样,有系统函数,也有自定义函数,我们简单介绍两个系统函数
basename
返回完整路径最后/的部分,通常用来获取文件名
basename [pathname] [suffix]
加上suffix会去除文件类型
[root@yellowstarEdu01 shcode]# basename /home/aaa/test.txt
test.txt
[root@yellowstarEdu01 shcode]# basename /home/aaa/test.txt .txt
test
dirname
返回完整路径最后/的前面的部分,用于返回文件路径
dirname [pathname]
[root@yellowstarEdu01 shcode]# dirname /home/aaa/test.txt
/home/aaa
自定义函数
#!/bin/bash
# 写一个函数,计算两数之和
function getSum()
{
SUM=$[$n1+$n2]
echo 两数之和为:$SUM
}
# 从控制台获取两个值
read -p "请输入第一个数:" n1
read -p "请输入第二个数:" n2
# 使用函数
getSum $n1 $n2
# 输出
[root@yellowstarEdu01 shcode]# ./myfun.sh
请输入第一个数:10
请输入第二个数:30
两数之和为:40
定时备份数据库
Shell编程的语法咱也学了那么多了,现在是时候做个小demo巩固一下所学的知识了。
之前也就说了,作为一个Java开发工程师,有时候自己做做小网站发布到服务器,需要做一些数据库的备份,那我们就来做一个定时备份数据库的shell脚本
开发要求
- 每天凌晨2:30备份数据库myorder到/data/backup/db
- 备份开始和备份结束能给给出相应的提示
- 备份后的文件要求以备份时间为文件名,并打包成
*.tar.gz
的形式,如2022-02-09_105710.tar.gz
- 为了不占用太多的内存空间,在备份的同时检查是否存在10天前备份的文件,如果有则删除它
分析
因为备份数据库需要的权限比较高,我们讲shell脚本放在/usr/sbin
目录下,只有管理员可以执行
每日凌晨自动备份
使用任务调度即可解决
备份开始和结束给出相应提示
这个不重要,echo输出即可
备份后的文件要求以备份时间为文件名,并打包成*.tar.gz
的形式
这个较为复杂,我们结合以下图片理解
删除10天以前的备份文件
使用find命令找到10天前的文件删除即可
解决过程
编写shell脚本
#!/bin/bash
#备份目录
BACKUP=/data/backup/db
#当前时间
DATETIME=$(date +%Y-%m-%d_%H%M%S)
echo $DATETIME
#数据库地址
HOST=localhost
#数据库用户名
DB_USER=root
#数据库密码
DB_PWD=123456
#备份的数据库名
DATABASE=myorder
#创建备份目录,如果不存在就创建
[ ! -d "${BACKUP}/${DATETIME}" ] && mkdir -p "${BACKUP}/${DATETIME}"
#备份数据库
mysqldump -u${DB_USER} -p${DB_PWD} -h${HOST} -q -R --databases ${DATABASE} | gzip > ${BACKUP}/${DATETIME}/$DATETIME.sql.gz
#将文件处理为tar.gz格式
cd ${BACKUP}
tar -zcvf $DATETIME.tar.gz ${DATETIME}
#删除对应的备份目录
rm -rf ${BACKUP}/${DATETIME}
#删除十天以前的备份文件
find ${BACKUP} -mtime +10 -name "*.tar.gz" -exec rm -rf {} \;
#备份完成提示
echo 数据库备份完成$DATETIME
开启任务调度
crontab -r # 编辑,输入以下语句
30 2 * * * /usr/sbin/mysql_db_backup.sh
# 查看任务调度
[root@yellowstarEdu01 sbin]# crontab -l
30 2 * * * /usr/sbin/mysql_db_backup.sh