提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、先搞懂:Shell到底是什么?
- 第一个Shell脚本:Hello World
- 脚本的两种执行方式
- 二、Shell变量:新手坑最多的地方
- 1. 系统自带的变量
- 2. 自定义变量:记住“等号两边没空格”
- 3. 环境变量:全局生效的变量
- 4. 位置参数变量:给脚本传参数
- 5. 预定义变量:Shell提前定义好的
- 三、Shell运算符:算数运算怎么写
- 四、条件判断:逻辑控制的基础
- 基本语法
- 常用判断条件
- (1)字符串比较
- (2)整数比较
- (3)文件权限判断
- (4)文件类型判断
- if分支语句
- case分支语句
- 五、循环语句:批量操作的神器
- 1. for循环
- 写法1:遍历参数/列表
- 写法2:类C语言循环
- 2. while循环
- 六、read读取输入:让脚本可以交互
- 七、Shell函数:封装复用代码
- 1. 两个超实用的系统函数
- 2. 自定义函数
- 八、实战案例:MySQL定时自动备份
- 需求
- 脚本实现
- 设置定时执行
- 总结
- 1、脚本基础
- 2、四大变量
- 3、算数运算
- 4、条件判断
- 分支语句
- 5、循环
- 6、交互输入read
- 7、函数
- 8、企业实战:MySQL定时备份
前言
这篇博客我就把Shell编程从入门到实战的核心内容全部整理出来,包括新手最容易踩的语法坑、高频必学的语法点、还有一个企业里真的会用的MySQL定时备份实战案例。全是自己踩坑总结出来的干货,建议收藏,学的时候对着敲一遍,基本就能上手用哩
一、先搞懂:Shell到底是什么?
很多人学了半天Shell,都没搞明白它到底是啥。其实一句话就能说清:Shell就是一个命令行解释器,是我们和Linux内核沟通的翻译官。
我们在终端敲的cd、ls这些命令,本质上都是先发给Shell,再由Shell解析后调用内核执行。而Shell编程,就是把一堆要执行的Linux命令,按照逻辑顺序写进一个文件里,让系统自动批量执行,不用我们手动一条一条敲。
Linux里最常用的Shell就是Bash,也是我们学习的重点。我们写的脚本文件通常以.sh结尾,这只是个约定俗成的标识,其实不影响执行,但规范起见还是建议加上。
第一个Shell脚本:Hello World
新手写第一个脚本,大概率会踩两个坑:忘了加脚本声明、忘了加执行权限。我当初两个坑全踩了,印象极其深刻。
一个标准的脚本长这样:
#!/bin/bash# 我的第一个Shell脚本echo"Hello World!"- 第一行
#!/bin/bash是必须的,告诉系统这个脚本要用Bash解释器来执行; #开头的是注释,脚本运行的时候会自动忽略。
脚本的两种执行方式
- 赋予权限后直接执行(推荐)
# 给脚本添加可执行权限chmodu+x hello.sh# 相对路径执行./hello.sh# 也可以用绝对路径执行/root/shellCode/hello.sh- 用sh命令直接调用(不用加权限)
shhello.sh这种方式本质是直接用sh解释器去读文件,所以不需要文件本身有执行权限,临时测试的时候很方便。
二、Shell变量:新手坑最多的地方
Shell的变量分为四大类:系统变量、自定义变量、环境变量、特殊变量,其中细节特别多,尤其是空格规则,新手一不注意就报错。
1. 系统自带的变量
Linux内置了很多现成的变量,比如$HOME(当前用户家目录)、$PWD(当前所在绝对路径)、$SHELL(当前解释器)、$USER(当前用户名)。
想查看所有系统变量,直接输set就行,会列出当前Shell里所有的变量和函数。
运行实例如下:
2. 自定义变量:记住“等号两边没空格”
这是我踩的第一个大坑!写A = 100报错了半天,后来才知道Shell里变量赋值,等号左右绝对不能有空格。
#!/bin/bash# 正确写法:等号两边无空格A=100# 输出变量要加$符号echoA=$Aecho"A=$A"# 撤销变量用unsetunsetAecho"撤销后A=$A"# 声明只读变量,不能修改也不能撤销readonlyB=2echo"只读变量B=$B"还有几个命名规则要记住:
- 变量名只能由字母、数字、下划线组成,不能以数字开头;
- 变量名习惯全大写,算是行业默认规范;
- 想把命令的执行结果赋值给变量,有两种写法:
# 写法1:反引号(不推荐,容易和单引号搞混)C=`date`# 写法2:$()(推荐,清晰易读)D=$(date)3. 环境变量:全局生效的变量
如果想让一个变量在所有Shell脚本里都能用,就要把它设成环境变量。
我们之前配置JDK的时候改的/etc/profile,里面写的就是全局环境变量。配置步骤很固定:
- 用
export 变量名=变量值把变量导出为环境变量; - 执行
source 配置文件让修改立刻生效,不用重启系统; - 用
echo $变量名就能查到变量值了。
4. 位置参数变量:给脚本传参数
我们写的脚本不可能永远写死内容,很多时候需要运行的时候传参数进去,这时候就要用到位置参数变量。
常用的就这几个:
| 变量 | 作用 |
|---|---|
$0 | 脚本本身的文件名 |
$n | 第n个输入参数,n从1开始,10以上要写成${10} |
$* | 所有输入参数,当成一个整体看待 |
$@ | 所有输入参数,每个参数相互独立 |
$# | 输入参数的总个数 |
举个例子就懂了,写个脚本position.sh:
#!/bin/bashecho"脚本文件名:$0"echo"第一个参数:$1"echo"第二个参数:$2"echo"所有参数(整体):$*"echo"所有参数(独立):$@"echo"参数总个数:$#"运行实例如下:
运行./position.sh 100 200,输出结果一目了然。这里$*和$@的区别,在后面for循环里会体现得更明显。
5. 预定义变量:Shell提前定义好的
这几个变量不用自己定义,直接就能用,写脚本判断状态的时候超实用:
$$:当前脚本运行的进程号(PID)$!:后台运行的最后一个进程的进程号$?:上一条命令的执行状态,0代表执行成功,非0代表执行失败
尤其是$?,写脚本的时候判断上一步有没有执行成功,全靠它。
三、Shell运算符:算数运算怎么写
Shell里的算数运算不能直接写1+2,有三种固定写法,我个人最推荐第二种,省事又不容易错。
$((运算式))$[运算式]✅ 推荐expr 运算式
举个例子,计算3*(5+1):
#!/bin/bash# 写法1RESULT1=$((3*(5+1)))echo"结果1:$RESULT1"# 写法2:最简洁,推荐用RESULT2=$[3*(5+1)]echo"结果2:$RESULT2"# 写法3:expr,运算符两边必须有空格,乘号要转义TEMP=`expr5+1`RESULT3=`expr3\* $TEMP`echo"结果3:$RESULT3"运行实例如下:
运行结果为:
expr写法特别麻烦,空格、转义符很容易写错,日常写脚本基本都用
$[],简单粗暴。
四、条件判断:逻辑控制的基础
基本语法
[条件表达式]⚠️ 巨坑提醒:中括号两边必须有空格![a=b]这么写必错,正确写法是[ a = b ]。
条件为空的时候返回false,有内容就返回true,所以[ abc ]的结果是真,[ ]是假。
常用判断条件
(1)字符串比较
=:判断两个字符串是否相等!=:判断两个字符串是否不相等
(2)整数比较
-lt:小于(less than)-gt:大于(greater than)-le:小于等于-ge:大于等于-eq:等于-ne:不等于
(3)文件权限判断
-r:有读权限-w:有写权限-x:有执行权限
(4)文件类型判断
-f:存在且是普通文件-e:文件/目录存在-d:存在且是目录
if分支语句
if语句的结尾是fi,就是if反过来写,刚开始记的时候觉得特别有意思。
if分支语句中结尾
fi必须加
# 单分支if[条件]then执行代码fi# 多分支if[条件1]then代码1elif[条件2]then代码2else代码3fi# 判断文件是否存在if[-f文件的绝对路径]then代码fi举个经典例子:输入分数,判断等级
#!/bin/bashif[$1-ge80]thenecho"优秀"elif[$1-ge70]thenecho"良好"elif[$1-ge60]thenecho"及格"elseecho"不及格"fi运行结果如下:
case分支语句
case的结尾是esac,同样是反过来记。每个分支结尾要写两个分号;;。
case$变量名in"值1")代码1;;"值2")代码2;;*)默认执行的代码;;esac执行结果如下:
五、循环语句:批量操作的神器
1. for循环
for循环有两种常用写法,适用场景不一样。
写法1:遍历参数/列表
for变量in值1 值2 值3do循环体done这个写法特别适合遍历输入参数,这里就能看出$*和$@的区别了:
#!/bin/bashecho"----测试\$*----"foriin"$*"doecho"参数:$i"doneecho"----测试\$@----"foriin"$@"doecho"参数:$i"done运行./test.sh 1 2 3,$*只会输出一行“参数:1 2 3”,而$@会输出三行,分别是1、2、3。
运行结果如下:
写法2:类C语言循环
for((初始值;循环条件;变量变化))do循环体done运行结果如下:
经典例题:求1到100的和
#!/bin/bashSUM=0for((i=1;i<=100;i++))doSUM=$[$SUM+$i]doneecho"1-100的和是:$SUM"运行结果如下:
2. while循环
while循环和其他语言逻辑一样,条件满足就一直循环。
while[条件]do循环体done同样用求1到100的和举例:
#!/bin/bashSUM=0i=1while[$i-le100]doSUM=$[$SUM+$i]i=$[$i+1]doneecho"总和是:$SUM"六、read读取输入:让脚本可以交互
如果想让脚本运行的时候,提示用户输入内容,就要用到read命令。两个最常用的参数:
-p:指定提示文字-t:指定等待超时时间,单位秒,超时就不等了
举个例子:
#!/bin/bash# 提示用户输入,不限时间read-p"请输入第一个数字:"NUM1# 提示用户输入,10秒内没输入就跳过read-t10-p"请输入第二个数字(10秒内):"NUM2echo"两个数的和是:$[$NUM1+$NUM2]"七、Shell函数:封装复用代码
Shell里有系统自带的工具函数,也可以自己定义函数。
1. 两个超实用的系统函数
basename:从完整路径里抠出文件名basename/home/test/a.txt# 输出 a.txtbasename/home/test/a.txt .txt# 去掉后缀,输出 adirname:从完整路径里抠出文件所在的目录路径dirname/home/test/a.txt# 输出 /home/test
处理文件路径的时候,这两个函数巨好用,不用自己写字符串切割。
2. 自定义函数
Shell函数的语法和其他语言有点不一样:参数不用写在括号里,调用的时候直接跟在函数名后面。
#!/bin/bash# 定义函数functiongetSum(){SUM=$[$n1+$n2]echo"两数之和是:$SUM"}# 读取用户输入read-p"输入第一个数:"n1read-p"输入第二个数:"n2# 调用函数,参数直接写在后面getSum$n1$n2⚠️ 注意:函数必须定义在调用前面,Shell是从上往下执行的,先定义后调用。
八、实战案例:MySQL定时自动备份
学了这么多语法,总得做点实际的东西。这个MySQL备份脚本是企业里真的会用的,写完特别有成就感。
需求
- 每天凌晨2:30自动备份MySQL数据库;
- 备份文件以备份时间命名,打包成tar.gz格式;
- 自动删除10天前的旧备份文件;
- 备份开始和结束给出提示。
脚本实现
#!/bin/bash# 备份文件存放目录BACKUP=/data/backup/db# 获取当前时间,格式:年月日_时分秒DATETIME=$(date+%Y-%m-%d_%H%M%S)echo"=====开始备份,时间:$DATETIME====="# 数据库配置HOST=localhostDB_USER=rootDB_PW=你的数据库密码DATABASE=要备份的库名# 如果备份目录不存在,就自动创建[!-d"${BACKUP}/${DATETIME}"]&&mkdir-p"${BACKUP}/${DATETIME}"# 执行数据库备份并压缩mysqldump -u${DB_USER}-p${DB_PW}--host=${HOST}-q-R--databases${DATABASE}|gzip>${BACKUP}/${DATETIME}/${DATETIME}.sql.gz# 切换到备份目录,把整个文件夹打包成tar.gzcd${BACKUP}tar-zcvf$DATETIME.tar.gz${DATETIME}# 删掉原文件夹,只保留压缩包rm-rf${BACKUP}/${DATETIME}# 删除10天前的备份文件find${BACKUP}-atime+10-name"*.tar.gz"-execrm-rf{}\;echo"=====备份完成,文件:$DATETIME.tar.gz====="设置定时执行
脚本写好之后,用crontab设置定时任务就能全自动运行了:
# 编辑定时任务crontab-e# 添加一行:每天凌晨2点30分执行脚本302* * * /usr/sbin/mysql_db_backup.sh总结
Shell(Bash)是Linux命令行解释器,Shell脚本就是把多条Linux命令编写到.sh文件实现批量自动化执行,是运维自动化的核心工具。
1、脚本基础
- 脚本首行声明:
#!/bin/bash,指定Bash解释器。#代表注释。 - 两种执行方式
chmod u+x xxx.sh && ./xxx.sh:添加执行权限后运行(推荐)sh xxx.sh:直接调用解释器,不需要执行权限
2、四大变量
- 系统变量:
$HOME、$PWD、$USER,set查看全部系统变量。 - 自定义变量:
A=100,等号两边不能有空格;取值$A;撤销unset A;只读readonly B=2。
命令结果赋值:D=$(date)(推荐),不推荐反引号`date`。 - 环境变量:
export 变量名=值导出全局变量;source 配置文件生效。 - 位置参数变量
| 变量 | 作用 |
|---|---|
$0 | 脚本文件名 |
$1 $2...${10} | 第1、2、10个参数 |
$* | 全部参数,作为一整个字符串 |
$@ | 全部参数,每个参数独立(循环常用) |
$# | 传入参数总个数 |
- 预定义变量
$$:当前脚本PID$!:最后一个后台进程PID$?:上一条命令返回状态,0成功,非0失败,脚本判断必备。
3、算数运算
优先用$[表达式],简洁方便;也可用$((表达式));expr运算符前后必须空格,乘号\*转义。
res=$[3*(5+1)]4、条件判断
语法:[ 条件 ],中括号内侧必须留有空格。
- 字符串:
=相等、!=不等 - 整数:
-lt小于、-gt大于、-le小于等于、-ge大于等于、-eq等于、-ne不等于 - 文件:
-f普通文件、-d目录、-e存在;权限-r -w -x
分支语句
- if语句,结束标记
fi
if[条件];then...elif[条件];then...else...fi- case语句,结束标记
esac,每个分支末尾;;。
5、循环
- for循环
- 遍历:
for i in 值1 值2 值3; do ... done,区分"$*"与"$@" - C风格计数:
for((i=1;i<=100;i++));do ... done
- while循环:条件成立循环
while[条件]dodone6、交互输入read
-p提示文字,-t超时秒数
read-p"提示文字"numread-t10-p"10秒超时输入"num27、函数
- 内置工具
basename获取文件名dirname获取目录路径
- 自定义函数:必须先定义,后调用,参数直接跟函数名后面,不写括号。
functionfun(){}fun 参数1 参数28、企业实战:MySQL定时备份
- 使用
mysqldump做数据库备份,配合gzip压缩; find命令清理N天前过期备份;- 配合
crontab -e配置定时任务,实现定时自动备份。
高频踩坑汇总
- 变量赋值等号不能加空格
[ ]条件判断,括号内部两侧必须空格- if结尾
fi、case结尾esac不能遗漏 "$*"与"$@"双引号包裹下循环行为不一样- 函数必须写在调用代码之前
$?判断上一步执行结果,自动化脚本必备