1.1 shell 简介
Shell 是一个命令行解释器,它接收外层应用程序/用户命令,然后调用操作系统内核,进而控制硬件。
Shell 还是一个功能强大的变成语言,易编写、易调试、灵活性强
1.2 shell 解析器
shell 脚本特点是由 “.sh” 结尾,常用的解析器为 sh 、bash ,除此之外还有 nologin 、dash 、 tcsh 、csh 等
1.2.1 Centos 默认解析器为bash
1.2.2 bash 和 sh 的关系
- sh 是 bash 的软链接
- 在一般的linux系统当中(如redhat),使用sh调用执行脚本相当于打开了bash的POSIX标准模式
- 也就是说 /bin/sh 相当于 /bin/bash —posix
所以,sh跟bash的区别,实际上就是bash有没有开启posix模式的区别
1.3 shell脚本入门
shell 脚本就是将完成一个任务的所有命令按照执行的先后顺序,自上而下写入到一个文本文件中,然后给予执行权限。
1.3.1 脚本格式
脚本以 #!/bin/bash 开头(作用是指定解析器,例如此处指定 bash 解析器)
也可用 /bin/bash/env bash|python|perl
1.3.2 案例1:编写一个shell脚本
1.3.2.1 需求
创建一个shell脚本,输出 “helllo world!”
脚本内容:
#!/bin/bash
echo "helloworld!"
1.3.2.2 执行脚本
可以看到不管用 bash+相对路径/绝对路径 还是 sh+相对路径/绝对路径 都可以运行脚本,相对路径和绝对路径运行也没有区别
原因是使用第一种命令执行脚本时,是直接使用 sh 或 bash 解析器调用脚本,因此不需要权限。使用相对路径执行因为是自己执行所以需要执行权限。
可以用 chmod 赋予权限,然后再次
1.3.3 案例2:多命令操作
1.3.3.1 需求
在 /home/atguigu 目录下创建一个 banzhang.txt , 在 banzhang.txt 中增加 “I love cls”
脚本内容:
#!/bin/bash
mkdir -p /home/atguigu
cd /home/atguigu
echo ”I love cls“ >> banzhang.txt
常用时间命令
- date +%Y-%m-%d #年(以四位数字格式打印年份)月日
- date +%y-%m-%d #年(以两位数字格式打印年份)月日
- date +%T #年(以四位数字格式打印年份)月日
- date +%H:%M:%S #小时分钟秒
- date +%T #小时分钟秒
- date +%w #一周中的第几天
- date +%W #一年中的第几周
- date -d “+1 hour” #一个小时后
- date -d “-1 hour” #一个小时前
- date -d “+1day” #一天后
- date -d “-1 day” #一天前
1.4 Shell 变量
1.4.1 系统变量
1.4.1.1 常用系统变量
$HOME、$PWD、$SHELL、$USER 等
1.4.2 自定义变量
1.4.2.1 基本语法
1.4.2.1.1 定义变量: 变量=值
1.4.2.1.2 重新赋值 变量=值
1.4.2.1.3 撤销变量 : unset 变量
1.4.2.1.3 声明静态变量: readonly 变量 , 注意:不能unset
只有重启才能撤销
1.4.2.1.4 将局部变量提升为全局变量: export 变量
可以看到第一次执行脚本无法显示 变量D 的值,因为此时它还是一个局部变量
1.4.2.2 变量定义规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写
- 等号两侧不能有空格
- 在 bash 中, 变量默认类型都是字符串类型,无法直接进行数值运算
- 变量的值如果有空格,需要使用双引号或单引号括起来
1.4.3 特殊变量:$n
1.4.3.1 基本语法
$n 功能描述: n 为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数需要用大括号包含,如${10}
1.4.3.2 案例3:参数练习 $n
1.4.4 特殊变量:$
1.4.4.1 基本语法
$# 功能描述:获取所有输入参数个数,常用于循环
1.4.4.2 案例4:参数练习 $
1.4.5 特殊变量:$*、$@
1.4.5.1 基本语法
$* 功能描述:这个变量代表命令行中所有的参数, $把所有的参数看成一个整体
$@ 功能描述:这个变量也代表命令行中所有的参数,不过 $@ 把每个参数区分对待
1.4.5.2 案例5:参数练习 $*、$@
1.4.6 特殊变量: $?
1.4.6.1 基本语法
$? 功能描述:最后一次执行的命令的返回状态,如果这个变量的值为 0 ,证明上一个命令正确执行;如果这个变量的值非 0 (具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了
1.4.6.2 案例6:参数练习 $?
1.5 运算符
1.5.1 基本语法
- “$((运算式))”或”$[运算式]”
- expr +、-、*、/、% 加、减、乘、除、取余
注意:expr 运算符间要有空格
1.5.2 案例6:加减法及混合运算
3+2
3-2
(2+3)*4
采用 $[运算符]计算(2+3)*4
1.6 条件判断
1.6.1 基本语法
[ condition ](注意 condition 前后要有空格)
注意: 条件非空即为true,[ atguigu ]返回 true ,[] 返回 false
1.6.2 常用判断条件
(1) 两个整数之间比较
= 字符串比较
-lt 小于 (less than) -le 小于等于(less equal)
-eq 等于(equal) -gt 大于(greater than)
-ge 大于等于(greater equal) -ne 不等于 (not equal)
(2) 按照文件权限进行判断
-r 有读的权限(read) -w 有写的权限(write)
-x 有执行的权限 (execute)
(3) 按照文件类型进行判断
-f 文件存在并且是一个常规的文件(file)
-e 文件存在(existence) -d 文件存在并是一个目录(directory)
1.6.3 案例7:判断条件
(1) 23 是否大于等于 22
返回是0,说明上一条命令正确执行
(2) 23 是否小于等于 22
非0即为flase
(3) helloworld.sh 是否有写权限
(4) /home/atguigu/cls.txt 目录中的文件是否存在
(5) /home/atguigu/banzhang.txt 目录中的文件是否存在
(6) 多条件判断( && 表示前一条命令执行成功时,才执行后一条命令, || 表示上一条命令执行失败后,才执行下一跳命令)
1.7 逻辑判断
1.7.1 单if语句
适用范围:只需要一步判断,条件返回真干什么或条件返回假干什么
语句格式
if [ condition ] # condition 值为 true 或 flase
then
commands
fi
翻译成汉语大致意思时:
假如 条件为真
那么
执行 commands 代码块
结果
1.7.2 案例8:文件夹是否存在
假如在 /tmp下没有 /abc 这个文件夹,那么就创建一个
1.7.3 if-then-else 语句
适用范围:条件为真干什么,条件为假干什么
if [ condition ]
then
commands1
else
commands2
fi
翻译成中文大致是:
假如条件为真
那么
执行 commands1 代码块
否则
执行 commands2 代码块
结束
1.7.4 案例9:判断登陆用户
假如登陆用户是root,输出管理员你好
假如登陆的是普通用户输出,guest,你好
1.7.5 if-then-elif语句
适用范围,适用于两个以上的判断结果,也就是多余一个以上的判断条件
if [ condition ]
then
commands1
elif [ condition 2]
then
commands2
......
else
commandsX
fi
语句翻译成汉语大致为:
假如 条件1 为真
那么
执行代码块1
假如 条件2 为真
那么
执行代码块2
以此类推的N个条件及对应的执行代码块
否则 [ 以上所有条件没有一个满足的 ]
执行代码块X
结束
1.7.6 案例10:判断两个整数的关系
嵌套写法
非嵌套写法
1.7.7 if 的高级用法
(1)双小圆括号中可以植入数学表达式做数学运算
(2)双方括号可以在条件中使用通配符,做字符串匹配
编写脚本思路
例如:编写一个自动安装 nginx 的脚本
先用中文写出思路,再按照思路进行编写
1.8 shell循环控制语句
1.8.1 sleep N 脚本执行到该步休眠 N 秒
1.8.2 continue 跳过循环中的某次循环
当按顺序输出 1-9 跳过 5 ,命令如下:
1.8.3 break 退出循环
要求用户输入一个字母,当输入 Q 时退出
循环输出数字,间隔 3 秒
当循环执行一次后,停止从里向外数第二层循环
1.9 shell流程控制-for循环语句
- for循环
- for语法
- 循环控制
脚本在执行任务的时候,总会遇到需要循环执行的时候,比如说我们需要脚本每隔五分钟执行一次 ping 的操作,除了计划任务,我们还可以使用脚本来王城,那么我们就用到了循环语句。
1.9.1 for循环介绍
很多人把 for 循环叫做 条件循环 ,或者 for i in,其实前者说的就是 for 的特性,for 循环的次数和给予的条件是成正比的,比如给 for 十个条件, for 就循环十次;后者说的是 for 语句。
1.9.2 for语法
1.9.2.1 for语法(1)
for var in value1 value2 ......
do
commands
done
1.9.2.2 案例11:利用for循环输出 1-9
1.9.2.3 for语法(2)
C 式的 for 命令
for ((变量;条件;自增减运算 ))
do
代码块
done
1.9.2.4 案例12:利用 for 循环输出1-9(2)
for循环使用多个变量
for的赋值
循环的优点
1.10.1 while 循环介绍
while 在 shell 中也是负责循环的语句,和 for 一样 。因为功能一样,很多人在学习和工作中的脚本遇到循环到底该使用 for 还是 while 呢?很多人不知奥,就造就了有些人一遇到循环就一味的用 for 或 while 。我认为可以按照这种思路来使用,既知道循环次数就可以用 for ,比如一天需要循环 24 次;如果不知道代码要循环多少次,那就用while ,比如猜数字,每个人猜对一个数字的次数都是不能固定的,也是未知的。
1.10.2 while 循环语法
while [ condition ] #注意,条件为真while才会循环,条件为假,停止循环
do
commands
done
1.10.3 实例13:while 基础语法练习
(1)如果输出的值大于 0 ,则会每三秒输出一次大于
(2)只有输入“root”才会跳出程序,否则一直循环
(3)只要 没有 /tmp/baism 这个文件就一直回复 “not found”,有就直接跳出
(4)丈母娘选女婿
1.10.4 shell流程控制-while循环嵌套
可以嵌套 if for while
循环控制 sleep break continue
1)continue跳过
跳过5继续输出
2)break 退出
跳过5,且当输出到10时退出
3) sleep 循环节奏
让每输出一个数字三秒后再输出第二个
4)whlie嵌套for
九九乘法表
1.11 until 语句
- until介绍
- until语法
- 案例分享
1.11.1 until介绍
和whlie正好相反,until是条件为假开始执行,条件为真停止执行
1.11.2 until语法
until [ condition ]
do
commands代码块
done
1.11.3 案例14:until练习
遍历10 - 20、
使用 while 循环打印1-10,使用 until 打印11-20
1.12 shell流程控制-case多条件分支语句
- case介绍
- case语法
- shell特殊变量
1.12.1 case介绍
在生产环境中,我们总会遇到一个问题需要根据不同的状况来执行不同的预案,那么我们要处理这样的问题就要首先根据可能出现的i情况写出对应的预案,根据出现的情况来加载不同的预案
1.12.2 case语法
case 变量 in
条件1)
执行代码块1;
;;
条件2)
执行代码块2;
;;
......
esac
注意:每个代码块执行完毕要以 ;; 结尾代表结束,case结尾要以倒过来写的esac来结束
1.12.3 案例15:case练习
输入1显示”haha“,输入2显示”hehe:”,输入其他数显示“1|2”
拜访丈母娘家遇到不同的人该说的话
1.12.4 shell 特殊变量
- $*:代表所有参数,其间隔为 IFS 内定参数的第一个字元
- $@:与*类似,不同之处在于不参照 IFS
- $#:代表参数数量
- $:执行上一个命令的返回值
- $$:本身的 Process ID
- $:执行上一个背景指令的 PID
- $_:显示出最后一个执行的命令
- $N:shell的第几个外传参数
1.13 Shell 函数
- 函数介绍
- 函数语法
- 函数实例
1.13.1 函数介绍
在写代码时,很多人习惯从头写到结束,完成以后在一起测试,但是到测试阶段才发现错误很多,很难更改。
所以引入了函数这个概念,把代码模块化,一个模块实现一个功能,哪怕是一个很小的功能都可以,这样的话我们写代码就会逻辑上比较简单,代码量比较少,排错简单,这也就是函数的好处。
函数默认不会执行,除非调用。
函数的优点:
- 代码模块化,调用方便,节省内存
- 代码模块化,代码量少,排错简单
- 代码模块化,可以改变代码的执行顺序
1.13.2 函数的语法
语法一:
函数名(){
代码块
return N
}
语法二:
function 函数名{
代码块
return N
}
1.13.3 案例016:函数应用
定义两个函数并调用
1.14 shell 数组
- 数组介绍
- 基本数组
- 关联数组
- 案例解析
1.14.1 数组介绍
一个变量只能存储一个值,但是现实中又有很多值需要存储,那么变量就有些拘谨了。比如做一个学员信息表,一个班 50 个人,每个人 6 条信息 ,我们需要定义 300 个变量才能完成。如果把一个学生的信息分为六个部分:ID、姓名、性别、年龄、成绩、班级。如果使用数组的话只要对应定义 6 个变量就可以了。
1.14.2 基本数组
数组可以让用户一次赋予多个值,需要读取数据时只需要通过索引调用就可以方便读出了。
1.14.2.1 数组语法
数组名称=(元素1 元素2 元素3 ...)
1.14.2.2 数组读出
${数组名称[索引]}
索引默认时元素在数组中的排队编号,默认第一个从 0 开始
1.14.2.3 数组赋值
方法一:一次赋一个值
ARRAY1[0]='E'
ARRAY1[1]='F'
方法二:一次赋多个值
ARRAY1=('a' 'b' 'c' 'd')
1.14.2.4 查看数组
declare -a #查看系统声明过哪些数组
1.14.2.5 访问数组元素
echo ${ARRAY1[0]} 访问数组中的第一个元素
echo ${ARRAY1[@]} 访问数组中的所有元素
echo ${ARRAY1[*]} 访问数组中的所有元素
echo ${#ARRAY1[@]} 统计数组元素的个数
echo ${!ARRAY2[@]} 获取数组元素的索引
echo ${ARRAY1[@]:1} 从数组下标 1 开始
echo ${ARRAY1[@]:1:2} 从数组下标 1 开始,访问两个元素
1.14.2.6 遍历数组
默认数组通过数组元素的个数进行遍历
#echo ${ARRAY1[0]}
pear
#echo ${ARRAY1[1]}
apple
#echo ${ARRAY1[2]}
orange
#echo ${ARRAY1[3]}
peach
针对关联数组可以通过数组元素的索引进行遍历
1.14.3 关联数组
关联数组可以允许用户自定义数组的索引,这样用起来更方便高效。
1.14.3.1 定义关联数组
声明关联数组变量
declare -A ass_array1
declare -A ass_array2
1.14.3.2 关联数组赋值
方法一:一次赋一个值
数组名[索引]=变量值
ass_array1[index1]=pear
ass_array1[index2]=apple
ass_array1[index3]=orange
ass_array1[index4]=peach
方法二:一次赋多个值
ass_array2=([index1]=tom [index2]=jack[index3]=alice[index4]=’bash shell’)
1.14.3.3 查看数组
declare -A
declare -A ass_array1=’([index4]=”peach” [index1]=”pear” [index2]=”apple” [index3]=”orange”)’
declare -A ass_array2=’([index4]=”bash shell” [index1]=”tom” [index2]=”jack” [index3]=”alice”)’
1.14.3.4 访问数组元素
echo ${ass_array2[index2]} 访问数组中的第二个元素
echo ${ass_array2[@]} 访问数组中所有元素
echo ${ass_array1[*]} 访问数组中所有元素
echo ${#ass_array2[@]} 获得数组元素的个数
echo ${!ass_array2[@]} 获得数组元素的索引
1.14.3.5 遍历数组
通过数组元素的索引进行遍历,针对关联数组可以通过数组元素的索引进行遍历
#echo ${ass_array2[index1]}
tom
#echo ${ass_array2[index2]}
jack
#echo ${ass_array2[index3]}
alice
#echo ${ass_array2[index4]}
bash shell
1.14.4 案例16:学员信息系统