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)

image-20220316174846536

[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为只读变量

image-20220316175106722

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

image-20220316181020739

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
posted @ 2022-03-20 22:55  90啊  阅读(266)  评论(0编辑  收藏  举报