linux之Shell详述
目录
Shell
Shell是一门编程语言。学习Shell的目的是提高(批量)执行多条命令的效率。
1、学习Shell之前需要了解的问题
1.1 编译型语言和解释型语言
编译型语言:需要编译器将代码编译成二进制文件,例如:C语言、Java语言、Golang语言。
解释型语言:需要解析器在代码需要运行的时候将代码编译成二进制数据,例如:Python、Shell。
1.2 强一致性和弱一致性
强一致性是变量必须跟设置数据类型保持一致。
弱一致性是变量可以根据不同场景自动变换自己的数据类型。
2、Shell语言入门
2.1 Shell代表的两层意思
1.shell指的是它本身这门语言。
2.服务器中的命令解析器,例如:bash、sh
[root@mysql01 ~]# chsh -l # 查看服务器已安装的解析器
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
2.2 Shell程序书写规范
# 注意:
1.shell脚本通常以`.sh`结尾
2.需要在shell脚本的第一行指定解析器
#!/bin/bash
命令
2.3 Shell中的变量
# 什么是变量
量:指的是记录事物的状态。
变:指的是事物的状态可以发生改变。
# 为什么要使用变量?
程序运行过程中本质上就需要一些列的变化。
# 变量的使用原则:
先定义,后使用。
2.3.1 定义:变量名=值
[root@dxd ~]# name="张飞"
# 注意:等号两边不能有空格(在shell解析器中,认为空格代表分隔符)
2.3.2 引用:使用$符取值
[root@dxd ~]# name="张飞"
[root@dxd ~]# echo $name
张飞
2.3.3 删除:unset 变量名
[root@dxd ~]# echo $name
张飞
[root@dxd ~]# unset name
[root@dxd ~]# echo $name
2.3.4 变量的命名规则
1.以字母开头:name
2.使用中划线或者下划线做单词之间的链接:city_name;city-name
3.同类型的变量用数字区分:name1;name2
4.变量名区分大小写,大小写不同的话就不是同一个变量:student;Student
2.3.5 变量值的来源
# 1.直接赋值
[root@dxd ~]# name="张飞"
# 2.从位置参数中获取:例如在脚本执行时,可以在执行脚本后面跟参数
[root@dxd ~]# ./first.sh n m g 3 4 4 4 4 5 5 5 5 5
n
m
g
5
[root@dxd ~]# vim first.sh
#!/bin/bash
echo $1
echo $2
echo $3
echo ${11}
[root@mysql01 ~]# chmod +x ./first.sh
# 3.用户输入
[root@dxd ~]# ./first1.sh
请输入:345
345
[root@dxd ~]# cat first1.sh
#!/bin/bash
read -p "请输入姓名:" name
echo $name
2.3.6 系统预定义变量
预定义变量 | 解释 |
---|---|
$* | 代表所有的参数 |
$@ | 所有的参数 |
$# | 参数的个数 |
$$ | 当前进程的PID |
$? | 代表上一条命令的返回值(默认:0代表成功,其他代表失败,最大128) |
[root@mysql01 ~]# ./first3.sh 0 1 2 3 4 55 66
0 1 2 3 4 55 66
0 1 2 3 4 55 66
7
2607
0
[root@mysql01 ~]# ./first3.sh 0 1 2 3 "4 55 66"
0 1 2 3 4 55 66
0 1 2 3 4 55 66
5
2609
0
[root@mysql01 ~]# cat first3.sh
#!/bin/bash
echo $*
echo $@
echo $#
echo $$
echo $?
2.4 Shell中的常量
相对于变量来说,常量就是不可以变化的值。
[root@dxd ~]# x=100
[root@dxd ~]# readonly x
[root@dxd ~]# echo $x
100
[root@dxd ~]# x=200
-bash: x: readonly variable # 报错:提示x为只读变量
3、Shell中的数据类型
# 在学习shell之前,需要了解的几个问题
1.什么是数据?
数据即变量的值,如age=18,18则是我们保存的数据。
2.为什么需要这么多的数据类型?
数据类型就是用来描述变量值(数据变化的类型)的类型。为了尽可能的减少变量值占用空间的大小,提升空间效率。
3.1 数字类型
数字类型就是用来保存数字的。
[root@dxd ~]# age=10
[root@dxd ~]# salary=3.1
3.2 字符串类型
字符串类型就是用来保存字符的。
# 注意:
双引号(""):弱引用,保留特殊字符的含义。
单引号(''):强引用,取消特殊字符的含义。
[root@dxd ~]# name='张飞'
[root@dxd ~]# address='上海市青浦区xxx'
[root@dxd ~]# echo "$name 住在 $address"
张飞 住在 上海市青浦区xxx
[root@dxd ~]# echo '$name 住在 $address'
$name 住在 $address
3.3 数组
1.什么是数组?
数组就是一系列元素的集合,一个数组内可以存放多个元素。
2.为什么要用数组?
为了将多个元素汇总到一起,避免单独定义。
3.数组的分类
普通数组(列表):只能够使用整数作为索引。
关联数组(字典):可以使用字符串作为数组的索引。
3.4 普通数组
3.4.1 声明数组的四种方式
- 方式一:直接声明
[root@dxd ~]# array=("张飞", 18)
[root@dxd ~]# declare -a
declare -a array='([0]="张飞," [1]="18")'
- 方式二:指定索引
[root@dxd ~]# array=([2]=19 [1]="关羽" [0]="未婚")
[root@dxd ~]# declare -a
declare -a array='([0]="未婚" [1]="关羽" [2]="19")'
- 方式三:依次赋值
[root@dxd ~]# array[0]="已婚"
[root@dxd ~]# array[1]="刘备"
[root@dxd ~]# declare -a
declare -a array='([0]="已婚" [1]="刘备" [2]="19")'
- 方式四:利用执行命令的结果赋值
[root@dxd ~]# array1=(`ls`)
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="圣诞树.py")'
3.4.2 访问数组的两种方式
# 注意:正向索引从`0`开始,反向索引从`-1`开始。
- 方式一:正向索引
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="圣诞树.py")'
[root@dxd ~]# echo ${array1[2]}
mysql80-community-release-el7-3.noarch.rpm
- 方式二:反向索引
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="圣诞树.py")'
[root@dxd ~]# echo ${array1[-5]}
mysql80-community-release-el7-3.noarch.rpm
3.5 关联数组
3.5.1 声明关联数组的两种方式
- 方式一:赋值声明
[root@dxd ~]# declare -A info
[root@dxd ~]# info['name']='张飞'
[root@dxd ~]# info['age']=19
- 方式二:直接声明
[root@dxd ~]# declare -A info='([name]="关羽" [age]=18)'
3.5.2 访问关联数组
[root@dxd ~]# echo ${info["name"]}
关羽
4、变量值操作
4.1 获取变量值的长度
语法:${#变量名}
[root@dxd ~]# name='World'
[root@dxd ~]# echo ${#name}
5
[root@dxd ~]# echo $name | wc -L
5
[root@dxd ~]# echo $name | awk '{print length}'
5
4.2 切片
语法:${变量名:开始位置:步长}
[root@dxd ~]# echo ${name:1} # 从第二个位置往后
orld
[root@dxd ~]# echo ${name:1:2} # 从第二个位置往后取两位数
or
[root@dxd ~]# echo ${name::2} # 从第一个位置开始往后取两位数
Wo
4.3 截断
[root@dxd ~]# url='www.baidu.com'
[root@dxd ~]# echo ${url#www.}
baidu.com
[root@dxd ~]# echo ${url#*w.}
baidu.com
------------------
[root@dxd ~]# echo ${url%.com}
www.baidu
4.4 替换
[root@dxd ~]# echo $url
www.baidu.com
[root@dxd ~]# echo ${url/baidu/aliyun}
www.aliyun.com
4.5 变量的替代
${x:-临时变量信息}:如果变量不存在,则使用默认值。
[root@dxd ~]# echo $name1
[root@dxd ~]# echo ${name1-"西施"}
西施
[root@dxd ~]# name1="吕布"
[root@dxd ~]# echo ${name1-"西施"}
吕布
${x:=新的变量信息}:如果变量不存在,则使用默认值,并且将默认值赋值给变量。
[root@dxd ~]# echo $name1
[root@dxd ~]# echo ${name1:="关羽"}
关羽
[root@dxd ~]# echo $name1
关羽
${x:?设置变量提示信息}:如果执行的变量不存在,则使用默认的提示信息。
[root@dxd ~]# echo $dfsdfasdfd
-bash: dfsdfasdfd: 未找到命令
[root@dxd ~]# echo ${dsfsdafds:?"该变量未定义"}
-bash: dsfsdafds: 该变量未定义
${x:+有设置变量提示信息}:变量存在则打印相关信息
[root@dxd ~]# echo $name1
关羽
[root@dxd ~]# echo ${name1:+该变量存在}
该变量存在
[root@dxd ~]# unset name1
[root@dxd ~]# echo ${name1:+该变量存在}
4.6 let计算
[root@dxd ~]# n=100
[root@dxd ~]# let n=$n+1
[root@dxd ~]# echo $n
101
4.7 取命令的结果赋值给变量
反引号(``)
$():
[root@dxd ~]# pid=`ps -ef | grep 30043 | head -1 | awk '{print $3}'`
[root@dxd ~]# echo $pid
1112
[root@dxd ~]# pid=$(ps -ef | grep 30043 | head -1 | awk '{print $3}')
[root@dxd ~]# echo $pid
1112
5、元字符
元字符是指能够被Shell解析的特殊字符。
5.1 算数运算符
算数运算符主要是用来做一些简单运算。
1、运算符
+ : 加法运算
- : 减法运算
* : 乘法运算
/ : 除法运算
% : 取模运算
2、bc运算
[root@dxd ~]# echo `echo 1+1 | bc`
2
# 保留两位小数
[root@dxd ~]# echo `echo "scale=2;1.1/1.2" | bc`
.91
3、expr运算
[root@dxd ~]# echo `expr 5 / 3`
1
注意:expr只支持整数运算。
4、$(())运算
[root@dxd ~]# echo $((5 / 3))
1
注意:$(())只支持整数运算。
5、$[]运算
[root@dxd ~]# echo $[5/3]
1
注意:$[]只支持整数运算。
6、let
[root@dxd ~]# let res=5/3
[root@dxd ~]# echo $res
1
注意:let只支持整数运算。
5.2 测试运算符
主要是用来探测某一个文件的属性的。
1、测试文件状态
1.1、测试是否是目录
[root@dxd ~]# test -d /root/nginx-1.20.2
[root@dxd ~]# echo $?
0
[root@dxd ~]# test -d /root/first.sh
[root@dxd ~]# echo $?
1
[root@dxd ~]# [ -d /root/nginx-1.20.2 ]; echo $?
0
[root@dxd ~]# [ -d /root/first.sh ]; echo $?
1
1.2、测试文件是否非空
[root@dxd ~]# cat first.sh
#!/bin/bash
echo $$
sleep 100
[root@dxd ~]# cat first1.sh
[root@dxd ~]# [ -s /root/first.sh ];echo $?
0
[root@dxd ~]# [ -s /root/first1.sh ];echo $?
1
[root@dxd ~]#
1.3、测试文件是否是标准文件
[root@dxd ~]# [ -f /root/first.sh ];echo $?
0
[root@dxd ~]# [ -f /root/nginx-1.20.2 ];echo $?
1
1.4、判断文件是否可写
[root@dxd ~]# [ -w /root/1.txt ];echo $?
0
[root@dxd ~]# su - test
最后一次失败的登录:三 3月 9 15:10:14 CST 2022从 124.221.117.45ssh:notty 上
最有一次成功登录后有 42 次失败的登录尝试。
[test@dxd ~]$ [ -w /root/1.txt ];echo $?
1
1.5、判断文件是否可读
[test@dxd ~]$ [ -r /root/1.txt ];echo $?
1
[test@dxd ~]$ exit
logout
[root@dxd ~]# [ -r /root/1.txt ];echo $?
0
1.6、判断文件是否可执行
[root@dxd ~]# [ -x /root/first.sh ];echo $?
0
[root@dxd ~]# [ -x /root/1.txt ];echo $?
1
1.7、判断是否是软连接文件
[root@dxd ~]# [ -L /bin ];echo $?
0
[root@dxd ~]# [ -L /root ];echo $?
1
1.8、判断是否是一个磁盘文件
[root@dxd /]# [ -c /proc/tty ];echo $?
1
5.3 字符串测试运算符
主要是用来判断字符串是否相等。
1、判断两个字符串是否相等
[root@dxd /]# [ "aaa" == "bbb" ] ; echo $?
1
[root@dxd /]# [ "aaa" == "aaa" ] ; echo $?
0
2、判断两个字符串是否不相等
[root@dxd /]# [ "aaa" != "aaa" ] ; echo $?
1
3、判断字符串是否为空
[root@dxd /]# [ -z "" ];echo $?
0
[root@dxd /]# [ -z "123" ];echo $?
1
4、判断字符串是否不为空
[root@dxd /]# [ -n "123" ];echo $?
0
[root@dxd /]# [ -n "" ];echo $?
1
5.4 数值测试运算符
1、数值测试运算符
运算符 | 解释 |
---|---|
-eq | 等于 |
-ne | 不等于 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
-a | 并且 |
-o | 或者 |
2、等于(-eq)
[root@dxd ~]# [ 123 -eq 234 ];echo $?
1
[root@dxd ~]# [ 123 -eq 123 ];echo $?
0
3、不等于(-ne)
[root@dxd ~]# [ 123 -ne 234 ];echo $?
0
[root@dxd ~]# [ 123 -ne 123 ];echo $?
1
4、大于(-gt)
[root@dxd ~]# [ 123 -gt 234 ];echo $?
1
[root@dxd ~]# [ 1203 -gt 234 ];echo $?
0
5、小于(-lt)
[root@dxd ~]# [ 1203 -lt 234 ];echo $?
1
[root@dxd ~]# [ 123 -lt 234 ];echo $?
0
6、大于等于(-ge)
[root@dxd ~]# [ 123 -ge 234 ];echo $?
1
[root@dxd ~]# [ 234 -ge 234 ];echo $?
0
[root@dxd ~]# [ 2340 -ge 234 ];echo $?
0
7、小于等于(-le)
[root@dxd ~]# [ 234 -le 123 ];echo $?
1
[root@dxd ~]# [ 234 -le 1230 ];echo $?
0
[root@dxd ~]# [ 234 -le 234 ];echo $?
0
8、并且(-a)
[root@dxd ~]# [ 123 -eq 123 -a 234 -eq 234 ];echo $?
0
[root@dxd ~]# [ 123 -eq 123 -a 234 -eq 2304 ];echo $?
1
9、或者(-o)
[root@dxd ~]# [ 123 -eq 123 -o 234 -eq 2304 ];echo $?
0
5.5 关系运算符
运算符 | 解释 |
---|---|
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
&& | 并且 |
两个竖向 | 或者 |
; | 前后都执行 |
1、小于
[root@dxd ~]# (($x<100));echo $?
1
[root@dxd ~]# (($x==100));echo $?
0
2、大于
[root@dxd ~]# (($x>100));echo $?
1
[root@dxd ~]# (($x==100));echo $?
0
3、小于等于
[root@dxd ~]# (($x<=100));echo $?
0
4、大于等于
[root@dxd ~]# (($x>=100));echo $?
0
5、等于
[root@dxd ~]# (($x==100));echo $?
0
6、不等于
[root@dxd ~]# (($x!=100));echo $?
1
7、并且
[root@dxd ~]# echo 123 && echo 456
123
456
[root@dxd ~]# echso 123 && echo 456
-bash: echso: 未找到命令
8、或者
[root@dxd ~]# echso 123 || echo 456
-bash: echso: 未找到命令
456
[root@dxd ~]# echo 123 || echo 456
123
9、前后都执行
[root@dxd ~]# echo 123 ; echo 456
123
456
[root@dxd ~]# echso 123 ; echo 456
-bash: echso: 未找到命令
456
[root@dxd ~]# echo 123 ; ecsho 456
123
-bash: ecsho: 未找到命令
5.6 赋值运算符
运算符 | 解释 |
---|---|
= | 直接赋值 |
+= | 先加法后等于 |
*= |
先乘法后赋值 |
/= | 先除法后赋值 |
%= | 先取模后赋值 |
1、直接赋值
[root@dxd ~]# x=100
2、先加法后等于
[root@dxd ~]# let x+=1
[root@dxd ~]# echo $x
101
3、先乘法后赋值
[root@dxd ~]# let x*=2
[root@dxd ~]# echo $x
202
4、先除法后赋值
[root@dxd ~]# let x/=2
[root@dxd ~]# echo $x
101
5、先取模后赋值
[root@dxd ~]# let x%=3
[root@dxd ~]# echo $x
2
5.7 补充
[[]]: 与 [] 基本上一致,不同的是[[]]支持正则
[test@dxd ~]$ [[ "$USER" =~ ^t ]];echo $?
0
[test@dxd ~]$ [[ "$USER" =~ t$ ]];echo $?
0
[test@dxd ~]$ [ "$USER" =~ t$ ];echo $?
-bash: [: =~: binary operator expected
2
6、流程控制之IF
if是一个条件判断的流程控制。
6.1 单分支IF
语法:if 条件判断; then 判断体 fi
# 案例:判断一个小姐姐的年纪是否是18。
[root@dxd ~]# ./first.sh
是18岁
[root@dxd ~]# cat first.sh
#!/bin/bash
old=18
if [ $old -eq 18 ];then
echo "是18岁"
fi
6.2 双分支IF
语法:if 条件判断; then 判断体 else 判断体 fi
[root@dxd ~]# ./first.sh
是18岁
[root@dxd ~]# vim first.sh
[root@dxd ~]# ./first.sh
不是18岁
[root@dxd ~]# cat first.sh
#!/bin/bash
old=19
if [ $old -eq 18 ];then
echo "是18岁"
else
echo "不是18岁"
fi
6.3 多分支IF
语法:if 条件判断; then 判断体 elif 判断体 else 判断体 fi
[root@dxd ~]# cat first.sh
#!/bin/bash
read -p "请输入你的性别:" sex
if [ "$sex" == "男" ];then
echo "性别:男"
elif [ "$sex" == "女" ];then
echo "性别:女"
else
echo "保密"
fi
[root@dxd ~]# ./first.sh
请输入你的性别:男
性别:男
[root@dxd ~]# ./first.sh
请输入你的性别:
保密
[root@dxd ~]#
6.4 案例
# 2.传入一个文件路径,判断文件的类型
[root@dxd ~]# ./first1.sh
请输入文件路径:、^H、
不是一个路径
[root@dxd ~]# ./first1.sh
请输入文件路径:/root
是一个路径
[root@dxd ~]# cat first1.sh
#!/bin/bash
read -p "请输入文件路径:" paths
if [ -d $paths ];then
echo "是一个路径"
else
echo "不是一个路径"
fi
# 2.判断80端口是否正在被使用
[root@dxd ~]# cat first1.sh
#!/bin/bash
netstat -nutlp | grep -w '\b80\b' &>/dev/null
if [ $? -eq 0 ];then
echo "80端口正在被使用"
else
echo "80端口未被使用"
fi
[root@dxd ~]# ./first1.sh
80端口正在被使用
7、流程控制之case语句
语法:
case [变量] in
判断1)
判断之后需要执行的内容
;;
判断2)
判断之后需要执行的内容
;;
判断3)
判断之后需要执行的内容
;;
*)
其他需要执行的内容
esac
7.1案例
# 案例1:判断一个用户是否是超级用户
[root@dxd ~]# ./first.sh
请输入用户名:default
普通用户
[root@dxd ~]# ./first.sh
请输入用户名:test
未知用户
[root@dxd ~]# cat first.sh
#!/bin/bash
read -p '请输入用户名:' users
case $users in
root)
echo "超级管理员"
;;
default)
echo "普通用户"
;;
*)
echo "未知用户"
esac
# 案例2:编写nginx启动脚本
#!/bin/bash
# 需要实现Nginx的启动、重启、关闭等功能
read -p '请输入:' status
. /etc/init.d/functions
case $status in
start)
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -ne 0 ];then
/usr/sbin/nginx -c /etc/nginx/nginx.conf
if [ $? -eq 0 ];then
action "Nginx启动成功" /bin/true
else
action "Nginx启动失败" /bin/false
fi
else
action 'Nginx进程已经存在' /bin/true
fi
;;
stop)
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
if [ $? -eq 0 ];then
action "Nginx关闭成功" /bin/true
else
action "Nginx关闭失败" /bin/false
fi
;;
restart)
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reopen
if [ $? -eq 0 ];then
action "Nginx重启成功" /bin/true
else
action "Nginx重启失败" /bin/false
fi
;;
*)
echo "请选择:start、stop或者restart"
ecas
知识储备:
action命令是用来显示执行结果格式的命令,默认系统不支持`action`,需要执行`. /etc/init.d/functions`命令才行
8、流程控制之while语句
8.1 while循环控制语句
语法:
while 条件
do
循环体
done
8.2 until循环控制语句
语法:
until 条件
do
循环体
done
8.3 while与until语句的区别
while 条件为true时循环;until 条件为false时循环。
8.5 循环中的break和continue
关键字 | 解释 |
---|---|
break | 跳出本层循环 |
continue | 跳出本次循环 |
8.4 案例
# 案例:猜年龄
#!/bin/bash
old=18
while true
do
read -p '请输入年龄:' num
if [ "$old" == "$num" ];then
echo "猜对了"
break
elif [ $old -gt $num ];then
echo "猜小了"
else
echo "猜大了"
fi
done
# 案例:测试break和continue
#!/bin/bash
while true
do
echo "第一层开始"
while true
do
sleep 1
echo "第二层开始"
break
echo "第二层结束"
done
echo "第一层结束"
sleep 1
done
# 案例:监控服务器
#!/bin/bash
fails=0
while [ $fails -lt 3 ]
do
ping -c 1 -t 1 172.17.16.20 &>/bin/null
if [ $? -ne 0 ];then
let fails++
fi
done
echo "探测失败 $fails 次"
[root@dxd ~]# ./first.sh
探测失败 3 次
9、流程控制之for循环
语法:
# shell风格的语法
for 变量 in [取值列表]
do
循环体
done
# C语言风格的语法
for (( 初始值; 条件; 步长))
do
循环体
done
9.1 案例
# 案例:输出10个数
#!/bin/bash
for i in {1..10}
do
echo $i
done
for (( i=1; i<=10; i++))
do
echo $i
done
# 案例:探测当前网段有哪些IP可用
#!/bin/bash
for i in {1..254}
do
ping -c 1 -W 1 172.17.16.$i &>/dev/null
if [ $? -eq 0 ];then
echo "172.17.16.$i 被占用"
else
echo "172.17.16.$i 可用"
fi
done
# 案例:测试/root目录中各种类型的文件的个数
#!/bin/bash
file=0
dir=0
all=0
for i in `ls /root`
do
if [ -f /root/$i ];then
let file++
elif [ -d /root/$i ];then
let dir++
else
let all++
fi
done
echo " 普通文件有 $file 个;普通文件夹有 $dir 个; 其他类型文件有 $all 个"
10、流程控制之select语句
select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择。
语法:
select 变量 in [值列表]
do
循环体
done
10.1 案例
# 案例:选择你的爱好
#!/bin/bash
PS3='请选择你的爱好:'
select i in '吃' '睡' '玩'
do
echo "你选择的是 $i "
done
# 案例:编写一个Nginx启动脚本,需要实现Nginx的启动、重启、关闭等功能
#!/bin/bash
. /etc/init.d/functions
PS3="请选择操作Nginx的状态:"
select status in start stop restart quit
do
if [ "$status" == "quit" ];then
break
fi
case $status in
start)
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -ne 0 ];then
/usr/sbin/nginx -c /etc/nginx/nginx.conf
if [ $? -eq 0 ];then
action "Nginx启动成功" /bin/true
else
action "Nginx启动失败" /bin/false
fi
else
action 'Nginx进程已经存在' /bin/true
fi
;;
stop)
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
if [ $? -eq 0 ];then
action "Nginx关闭成功" /bin/true
else
action "Nginx关闭失败" /bin/false
fi
;;
restart)
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reopen
if [ $? -eq 0 ];then
action "Nginx重启成功" /bin/true
else
action "Nginx重启失败" /bin/false
fi
;;
*
echo "请选择:start、stop或者restart"
esac
done
11、函数
# 什么是函数?
函数就是用来盛放一组代码的容器,函数内的一组代码一定具备某种特定的功能,称之为一组代码块。
调用函数便可以触发函数内的代码块。这个过程可以实现代码的复用。
# 为什么要用函数?
1、为了减少代码冗余
2、提升代码的组织结构性、可读性
3、增强拓展性
# 函数必须遵守的原则?
先定义,后调用。
11.1 函数的语法
# 语法一:推荐使用
function [函数名称] () {
函数体
}
# 语法二:
function 函数名 {
函数体
}
# 语法三:
函数名 {
函数体
}
11.2 案例
# 案例:计算1+1=2
#!/bin/bash
function add() {
let n=1+1
echo $n
}
function add1 {
let n=1+1
echo $n
}
add2 {
let n=1+1
echo $n
}
11.3 调用函数
11.3.1 有参数函数
# 直接在函数名之后传入即可。
[root@dxd ~]# ./first.sh
2
[root@dxd ~]# vim first.sh
#!/bin/bash
function add() {
let n=$1+$2
echo $n
}
add 1 2
11.3.2 无参数函数
[root@dxd ~]# ./first.sh
2
[root@dxd ~]# vim ./first.sh
#!/bin/bash
function add() {
let n=1+1
echo $n
}
add
11.4 案例
# 案例:实现一个简单的计算器
#!/bin/bash
function j() {
let n=$1+$2
echo $n
}
function ja() {
let n=$1-$2
echo $n
}
function c() {
let n=$1*$2
echo $n
}
function cu() {
let n=$1/$2
echo $n
}
PS3="请选择计算模式:"
select i in + - x /
do
case $i in
+)
j $1 $2
break
;;
-)
ja $1 $2
break
;;
x)
c $1 $2
break
;;
/)
cu $1 $2
break
;;
*)
echo "error"
esac
done
# 案例:实现Nginx脚本
#!/bin/bash
. /etc/init.d/functions
function start() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
action "Nginx进程已存在" /bin/true
else
/usr/sbin/nginx -c /etc/nginx/nginx.conf
if [ $? -eq 0 ];then
action "Nginx启动成功" /bin/true
else
action "Nginx启动失败" /bin/false
fi
fi
}
function stop() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
if [ $? -eq 0 ];then
action "Nginx关闭成功" /bin/true
else
action "Nginx关闭失败" /bin/false
fi
else
action "Nginx未启动" /bin/false
fi
}
function restart() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
stop
start
else
start
fi
}
PS3="请选择Nginx操作模式:"
select i in start stop restart quit
do
case $i in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
quit)
break 2
;;
*)
echo "请选择:start、stop或者restart"
esac
done
11.5 返回值(return)
# 函数中的返回值一般是[0 ~ 128]
#!/bin/bash
. /etc/init.d/functions
function start() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
action "Nginx进程已存在" /bin/true
else
/usr/sbin/nginx -c /etc/nginx/nginx.conf
if [ $? -eq 0 ];then
action "Nginx启动成功" /bin/true
else
action "Nginx启动失败" /bin/false
return 1
fi
fi
return 0
}
function stop() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
if [ $? -eq 0 ];then
action "Nginx关闭成功" /bin/true
else
action "Nginx关闭失败" /bin/false
return 1
fi
else
action "Nginx未启动" /bin/false
fi
return 0
}
function restart() {
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ];then
stop && start
[ $? -eq 0 ] && return 0 || return 1
else
start
[ $? -eq 0 ] && return 0 || return 1
fi
}
PS3="请选择Nginx操作模式:"
select i in start stop restart quit
do
case $i in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
quit)
break 2
;;
*)
echo "请选择:start、stop或者restart"
esac
done
12、数组
# 数组分类
1、普通数组
2、关联数组
# 为什么要用数组?
将具有相同属性的一组元素集合到一起,避免单独定义麻烦。
12.1 数组的定义
# 普通数组的定义方式
declare -a array=(1 2 3 4 5 6 7 8 9)
# 关联数组的定义方式
declare -A array1=([name]=shanhe)
12.2 数组的访问
普通数组:正向索引从0开始,反向索引从-1开始。
关联数组:按照下标。
12.4 遍历数组
# 知识储备:
取所有值:${array[*]}
取所有键:${!array1[*]}
12.4.1 遍历普通数组
#!/bin/bash
declare -a array=(1 2 3 4 5 6 7 8 9)
for i in ${array[*]}
do
echo $i
done
12.4.2 遍历关联数组
[root@dxd ~]# for i in ${!array1[*]};
do
echo ${array1[$i]};
done
[root@dxd ~]# for i in ${array1[*]}
do
echo $i
done
12.4.3 案例
# 案例:通过数组判断当前文件夹中的各种文件类型的个数
[root@dxd ~]# cat first2.sh
#!/bin/bash
declare -a array=(`ls`)
dir=0
file=0
for i in ${array[*]}
do
[ -d $i ] && let dir++
[ -f $i ] && let file++
done
echo "普通文件有:$file 个,普通文件夹有:$dir 个。"
13、信号处理
13.1 HUP(1)信号
13.1.1 挂起
[root@dxd ~]# cat first3.sh
#!/bin/bash
echo $$
sleep 1000
[root@dxd ~]# kill -1 1993
13.1.2 重新加载配置
[root@dxd ~]# kill -s HUP 2277
等价
[root@dxd ~]# kill -1 2277
13.2 退出信号
- INT(2):由ctrl + c 发出的信号。
- QUIT(3):由 ctrl + \ 发出的信号。
- TSTP(20):由 ctrl + z 发出的信号。
# 知识储备:trap
#!/bin/bash
# 捕捉 ctrl + c 信号
trap "echo 已经捕捉到 ctrl + c信号"
sleep 1000
13.3 终止信号
- KILL(9):杀死信号
立即回收内存。
- TERM(15):停止信号
先通知进程终止,超时未终止的信号再杀死。
14、Systemctl管理脚本
systemctl配置文件路径:/usr/lib/systemd/system
14.1 systemctl包含的文件
*.service:操作服务文件
*.target:开机级别的服务文件
14.2 systemctl配置的格式
unit:主要是用来做服务说明的(没有实际意义)。
service:核心区域(各种服务操作【开启、停止、重启等】)
install:用来配置多用户
14.3 unit
Description:主要是用来简介。
After:表示该服务需要在那些服务后启动。
Before:表示在某些服务之前启动。
14.4 Service
- Type:表示在后台运行的模式
- User:表示服务运行的用户,默认:当前用户
- Group:表示运行的用户组,默认:当前用户
- PIDFile:指定PID文件路径
- ExecStart:指定服务的启动命令
- ExecReload:指定服务重启时执行的命令
- ExecStop:指定服务停止执行的命令,默认执行 kill -s TERM
- PrivateTmp:指定是否分配临时的独立空间
- Environment:指定环境变量的
- ExecStartPre:启动服务之前执行的命令
- ExecStartPost:服务启动之后执行的命令
- ExecStopPost:服务停止之后执行的命令
14.5 Install
# WantedBy=multi-user.target # 定支持多用户模式
[root@dxd system]# cat nginx2.service
[Unit]
Description=Nginx Service 测试服务文件
After=network.target
Before=mysqld.service
[Service]
Type=foking
User=root
Group=root
PIDFile=/run/nginx2.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStopPost=echo "Stop Success"
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/usr/bin/kill -s HUP $MAINPID
ExecStop=/usr/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
14.6 Type类型
- forking:需要被启动的进程拥有主进程和子进程。
- simple:启动命令产生进程就是主进程
- notify:类似于simple
# 通常情况下,在后台运行的命令使用forking;在前台运行的命令使用simple和notify等。
14.7 重启类型(Restart)
- on:一旦程序出现宕机,system不做任何管理 (默认)
- on-success:只有正常退出情况下,才会重启。
- on-failure:非正常退出时,重启
- on-abnaomal:只有信号终止或超时,才会重启
- always:不管什么原因退出,都会重启
# 通常情况下,采用on或者on-success两个参数。
14.8 案例:启动Nginx
[root@dxd ~]# vim nginx.sh
#!/bin/bash
. /etc/init.d/functions
args=$1
function print() {
[ $? -eq 0 ] && action "Nginx $args is " /bin/true || action "Nginx $args is " /bin/false
}
case $args in
start)
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -ne 0 ]; then
/usr/sbin/nginx -c /etc/nginx/nginx.conf
fi
print
;;
stop)
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ]; then
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
fi
print
;;
restart)
netstat -nutlp | grep 'nginx: master' &>/dev/null
if [ $? -eq 0 ]; then
/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
sleep 1
fi
/usr/sbin/nginx -c /etc/nginx/nginx.conf
print
;;
test)
/usr/sbin/nginx -c /etc/nginx/nginx.conf -t
print
;;
*)
echo "Please Enter start/stop/test/restart"
;;
esac
# Service
[root@dxd ~]# vim /usr/lib/systemd/system/nginx3.service
[Unit]
Description=Nginx Service 脚本服务文件
[Service]
Type=forking
ExecStartPre=/usr/local/bin/nginx.sh test
ExecStart=/usr/local/bin/nginx.sh start
ExecReload=/usr/local/bin/nginx.sh restart
ExecStop=/usr/local/bin/nginx.sh stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target