Shell脚本应用及实战演练
文章目录
- 一、Shell脚本语言的基本结构
- 1、Shell脚本的用途:
- 2、 Shell脚本基本结构:
- 3、 创建Shell脚本过程
- 4、 脚本注释规范
- 二、Shell脚本语言的变量用法详解
- 位置与预定义变量
- 三、 Shell字符串详解
- 1、Shell字符串拼接
- 2、Shell字符串截取
- 3、 Shell的格式化输出printf
- 4、条件测试命令
- 5、条件测试命令及其语法
- 6、文件测试表达式
- 7、整数测试表达式
- 8、关于()与 { }
- 9、使用read命令命令来接受输入
- 四、流程控制
- 4.1 条件选择
- 4.1.1 选择执行if语句
- 4.1.1.1 单分支
- 4.1.1.2 双分支
- 4.1.1.3 多分支
- 4.1.2条件判断case语句
- 4.2、循环
- 4.2.1、循环执行介绍
- 4.2.2、for循环
- 4.2.3、while循环
- 4.2.4、循环控制语句continue 、break
- 4.3、Shell中的数组
- 4.3.1、Shell数组的概念
- 4.3.2、Shell数组的定义
- 4.3.3、数组的基本定义
- 4.3.4、Shell数组的拼接合并
- 4.3.5、Shell删除数组元素
- 4.3.6、获取数组某范围的元素
- 5、Shell中的函数
- 5.1、Shell函数的定义
- 6、Shell编程之正则表达式
- 6.1、正则表达式
- 6.1.1、正则表达式概述
- 6.1.2、正则表达式的分类
- 五、shall实战
- 打印99乘法表
- 直角三角形
- 创建用户
- 猜数游戏
一、Shell脚本语言的基本结构
1、Shell脚本的用途:
- 自动化常用命令
- 执行系统管理和故障排除
- 创建简单的应用程序
- 处理文本或文件
2、 Shell脚本基本结构:
Shell脚本编程:是基于过程式,解释执行的语言
编程语言的基本结构:
- 各种系统命令的组合
- 数据存储:变量,数组
- 表达式:a+b
- 控制语句:if、case、for、while
shell脚本:包含一些命令或声明,并符合一定格式的文本文件
格式要求:首行执行shebang机制
#声明后续语句是通过那种语言写的
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
3、 创建Shell脚本过程
- 使用vim创建文本文件,第一行必须包括shell声明序列:
#!/bin/bash
- 加执行权限,给予执行权限,在命令行上指定脚本的绝对或相对路径
[root@localhost ~]# chmod +x shellScript/hello.sh
- 运行脚本,直接运行解释器,将脚本作为解释器程序的参数运行。
[root@localhost ~]# /root/shellScript/hello.sh
4、 脚本注释规范
- 第一行一般为调用使用的语言
- 程序名,避免更改文件名为无法找到正确的文件
- 版本号
- 更改后的时间
- 作者相关信息
- 该程序的作用,及注意事项
- 最后是各版本的更新简要说明
二、Shell脚本语言的变量用法详解
位置与预定义变量
位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数
$1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个
#预定义变量
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合成一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
$? 上个命令的退出状态,或函数的返回值
$$ 当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID注意:$@,$*只有被双引号括起来的时候才会有差异
三、 Shell字符串详解
字符串(String)就是一系列字符的组合。字符串是Shell编程中最常用的数据类型之一
字符串可以由单引号''
包围,也可以由""
包围,也可以不用引号,三种方式的区别
- 由单引号
' '
包围的字符串- 任何字符都会原样输出,在其中使用变量是无效的
- 字符串中不能出现单引号,即使对单引号进行转义也不行
- 由双引号
" "
包围的字符串- 如果其中包含了某个变量,那么该变量就会被解析(得到该变量的值),而不是原样输出
- 字符串中可以出现双引号,只要进行转义就行
- 不被引号包围的字符串
- 不被引号包围的字符串中出现变量也会被解析,这一点和双引号
""
包围的字符串一样 - 字符串中不能出现空格,否则空格后面的字符串会作为其他变量或者命令解析
获取字符串长度
- 不被引号包围的字符串中出现变量也会被解析,这一点和双引号
在Shell中获取字符串长度很简单,具体方法如下:
${#string_name}
string_name:表示字符串名字
1、Shell字符串拼接
在脚本语言中,字符串的拼接(也称为字符串连接或者字符串合并)往往都非常简单,在Shell中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接
2、Shell字符串截取
Shell截取字符串通常有两种方式,从指定位置开始截取和从指定字符(子字符串)开始截取
从指定位置开始截取
这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串
既然需要指定起始位置,那么就要涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数?答案是:Shell同时支持两种计数方式
1.从字符串左边开始计数
如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:
${string:start:length}其中,Sting是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)
例如:
url="c.biancheng.net"
echo ${url:2:9}>结果为:bianchengurl="c.biancheng.net"
echo ${url:2} #省略length,截取到字符串末尾>结果为:biancheng.net
2.从右边开始计数
如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
格式:
${string:0-start:length}
实例
url="c.biancheng.net"
echo ${url:0-13:9}>结果为:biancheng 从右边数:b是第13个字符url="c.biancheng.net"
echo ${url:0-13} #省略length,直接截取到字符串末尾
>结果为:biancheng.net
3、 Shell的格式化输出printf
%f浮点格式,保留小数点位数%.nf,n为数字**
常用转义字符:
转义符 | 功能 |
---|---|
\a | 警告字符,通常为ASCII的BEL字符 |
\b | 后退 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 表示\本身 |
实现算数运算
1. let var=算术表达式
2. var=$[算术表达式]
3. var=$((算术表达式))
4. var=$(expr arg1 arg2 arg3 ...)
5. declare -i var = 数值
6. echo '算术表达式' | bc (支持浮点数)
**内建的随机数生成器变量:
$RANDOM 取值范围:0-32767
4、条件测试命令
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,实现评估布尔声明,以便在条件性环境下进行执行。
- 若真,则状态码变量$?返回0
- 若假,则状态码变量$?返回1
5、条件测试命令及其语法
语法1:test <测试表达式> 说明:test命令和<测试表达式>之间至少有一个空格
在shell中,大于用 -gt 表示,小于用 -lt 表示,大于或等于用 -ge 表示,小于或等于用 -le表示 ,不相等用-ne 表示
[root@ansible-salve1 ~]# test 1 -lt 2
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]# test 2 -lt 1
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]#
6、文件测试表达式
常用的文件测试操作符 | 说明 |
---|---|
-a/-e 文件 | 文件是否存在 |
-d 文件 | 文件存在且为目录则为真,即测试表达式成立 |
-f 文件 | 文件存在且为普通文件则为真,即测试表达式成立 |
-r 文件 | 文件存在且可读为真 |
-w 文件 | 文件存在且可写为真 |
-x 文件 | 文件存在且可执行则为真 |
-z字符串若字符串的长度为0,则为真,z可以理解为zero
“字符串1” == ”字符串2“若字符串1长度等于字符串2长度,则为真
“字符串1” != ”字符串2“若字符串1长度不等于字符串2长度,则为真
“字符串1” =~ “字符串2”左侧字符串是否能被右侧的PATTERN所匹配。
7、整数测试表达式
在[ ] 或 test中使用的比较符号 | 在(()) 或 [[ ]]中使用的比较符号(不用这个做数字比较) | 说明 |
---|---|---|
-eq | == 或 = | 相等,equal |
-ne | != | 不相等,not equal |
-gt | > | 大于,greater than |
-ge | > = | 大于等于,greater equal |
-lt | < | 小于,less than |
-le | < = | 小于等于,less equal |
8、关于()与 { }
( )和 { }都可以将多个命令组合再一次,批量执行,{ } 里的内容需要与两侧用空格隔开并在命令结尾加上;
- ( )会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境
9、使用read命令命令来接受输入
read 是 Shell 内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据
选项:
Option | 说明 |
---|---|
-a array | 把读取的数据赋值给数组array,从下标0开始 |
-d delimiter | 把字符串delimiter指定读取结束的位置,而不是一个换行符(读取的数据不包括delimiter) |
-e | 在获取用户输入的时候,对功能键进行编码转换,不会直接显示功能键对应的字符 |
-n num | 读取num个字符,而不是整行字符 |
-p prompt | 显示提示信息,提示内容为prompt |
-r | 原样读取(Raw mode),不会把反斜杠字符解释为转义字符 |
-s | 静默模式(Silent mode),不会再屏幕上显示输入的字符。例如:输入密码 |
-t seconds | 设置超时时间,单位为秒。如果用户没能按时完成,返回一个非0的退出状态 |
-u fd | 使用文件描述符fd作为输入源,而不是标准输入,类似于重定向 |
四、流程控制
4.1 条件选择
4.1.1 选择执行if语句
if结构:
[root@ansible-salve1 shell]# help if
if: if 条件; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] fi根据条件执行命令。`if COMMANDS'列表被执行。如果退出状态为零,则执行`then COMMANDS' 列表。否则按顺序执行每个 `elif COMMANDS'列表,并且如果它的退出状态为零,则执行对应的 `then COMMANDS' 列表并且 if 命令终止。否则如果存在的情况下,执行 `else COMMANDS'列表。整个结构的退出状态是最后一个执行的命令的状态,或者如果没有条件测试为真的话,为零。退出状态:返回最后一个执行的命令的状态。
[root@ansible-salve1 shell]#
4.1.1.1 单分支
if [ 条件判断式 ];then命令
fi 或者if [ 条件判断式 ]then命令
fi
4.1.1.2 双分支
if [ 条件判断式 ]then命令
else命令
fi
4.1.1.3 多分支
if [ 条件判断式1 ]then命令
elif [ 条件判断式2 ]then 命令
...
...
else命令
fi
说明:
- 多个if条件时,逐个条件进行判断,第一次遇见为“真”条件时,执行其分支,而后结束整个if语句
- if语句可嵌套
4.1.2条件判断case语句
格式
case 变量引用 in
PAT1)分支1;;
PAT2)分支2;;
...
*)默认分支;;
esac
实例:
#!/bin/bash
cat <<EOF
请选择:
1.备份文件
2.清理日志文件
3.软件升级
4.软件回滚
5.删库跑路
EOF
read -p "请输入上面的数字1-5:" MENU
case $MENU in
1)./backup.sh;;
2)echo "清理日志";;
3)echo "软件升级";;
4)echo "软件回滚";;
5)echo "删库跑路";;
*)echo "Input False"
esac
[root@bogon ~]# bash 1.sh
请选择:
1.备份数据库
2.清理日志文件
3.软件升级
4.软件回滚
5.删库跑路
请输入上面的数字1-5:1
备份成功
4.2、循环
4.2.1、循环执行介绍
将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
- 循环次数事先已知
- 循环次数事先未知
常见的循环的命令:for,while
#循环的逻辑:程序先进行语句判断,如果为真则执行循环语句,然后再进行语句判断,直至语句判断失败才跳出
4.2.2、for循环
格式:
第一种写法
for NAME [in words ...]; do commands;done# 第二种写法
for 变量 in 列表循环体
done# 第三种写法
for 变量 in 列表
do循环体
done
4.2.3、while循环
while command; do commands;donewhile condition;do 循环体 done
无限循环
while true;do循环体
done
4.2.4、循环控制语句continue 、break
continue\[N\]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层格式:while CONDITION1;do循环体1...if command2;thencontinuefiCMDn....
done
break\[N\]:提前结束第N层后的全部循环;最内层为第1层,默认为1#!/bin/bash
for((i=0;i<10;i++));dofor((j=0;j<10;j++));do[ $j -eq 5 ] && breakecho $j doneecho ----------------------------done
4.3、Shell中的数组
4.3.1、Shell数组的概念
数组是若干数据的集合,其中存放的每一份数据都称为元素。Shell
不限制数组的大小,理论上可以存放无限量的数据,Shell
数组元素的下标也是从0开始计数
获取数组中的元素要使用下标[ ],下标可以是一个整数,也可以是一个结果为整数的表达式;下标必须大于等于0
注意:
Shell
只支持一维数组,不支持多维数组
4.3.2、Shell数组的定义
4.3.3、数组的基本定义
在Shell
中,用小括号()
来表示数组,数组元素之间用空格来分隔
#arrayname=(1 2 3 4 5)
`输出定义数组中的全部元素
#echo ${arrayname[*]}
#echo ${arrayname[@]}
`输出定义数组中的第二个元素
#echo ${arrayname[0]}
`输出定义数组中的第二个元素
#echo ${arrayname[1]}
`输出定义数组中的元素个数
#echo ${#arrayname[*]}
4.3.4、Shell数组的拼接合并
array_new=(${array1[@]} ${array2[@]})
array_new=(${array1[*]} ${array2[*]})
`两种方式是等价的,选择其一即可。其中,array1 和 array2 是需要拼接的数组,array_new 是拼接后形成的新数组。
4.3.5、Shell删除数组元素
unset array_name[index]
`其中,array_name表示数组名,index表示数组下标unset array_name
`删除整个数组
4.3.6、获取数组某范围的元素
在Shell
中直接通过${数组名[@/*]:起始位置:长度}
获取数组给定范围内元素,返回字符串,中间用空格分开
#!/bin/bash
array=(yoona lucy tom)
echo ${array[*]}
echo ${array[*]:1:2}
echo ${array[@]:0:2}
-->结果为:
yoona lucy tom
lucy tom
yoona lucy
5、Shell中的函数
5.1、Shell函数的定义
Shell函数的本质是一段可以重复使用的脚本代码,这段代码被提前编好了,放在了指定位置,使用时直接调用即可
Shell 中的函数和C++、Java、Python、C# 等其它编程语言中的函数类似,只是在语法细节有所差别。
Shell 函数定义的语法格式如下:
function name() {statements[return value]
}
对各个部分的说明:
function
是 Shell 中的关键字,专门用来定义函数;name
是函数名;statements
是函数要执行的代码,也就是一组语句;return value
表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。
由{ }
包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
6、Shell编程之正则表达式
6.1、正则表达式
6.1.1、正则表达式概述
正则表达式通常用于判断语句中,用来检查某一字符串是否满足某一格式。
正则表达式是由普通字符与元字符组成。普通字符包括小写字母、数字、标点符号及一些其他符号。元字符是指在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
6.1.2、正则表达式的分类
正则表达式根据从POSIX BRE或者POSIX ERE标准可以分为基本正则表达式和扩展正则表达式。
基本正则表达式
支持的工具:grep
、egrep
、sed
、 awk
,注意grep
要配合-E
或者-P
使用。
元字符 | 含义及用法 |
---|---|
\ | 转义字符,用于取消特殊符号的含义,例: \! 、 \n 、 \$ 等 |
^ | 匹配字符串开始的位置,例:^a 、^the 、^# 、^[a-z] |
$ | 匹配字符串结束的位置,例: word$ 、^$ 匹配空行 |
. | 匹配除\n 之外的任意的一个字符,例: go.d 、g..d 。如果想要匹配包含\n字符可以使用 [.\n] |
* | 匹配前面子表达式0次或者多次,例: goo*d 、 go.*d |
[list] | 匹配list列表中的一个字符,例: go[ola]d ,[abc] 、[a-z] 、[a-z0-9] 、[0-9] 匹配任意一位数字 |
[^list] | 匹配任意非list列表中的一个字符,例:[^0-9] 、[^A-Z0-9] 、[^a-z] 匹配任意一位非小写字母 |
\{n\} | 匹配前面的子表达式n次,例: go\{2\}d 、 [0-9]\{2\} 匹配两位数字 |
\{n,\} | 匹配前面的子表达式不少于n次,例: gol{2,l}d 、[0-9]\{2,\} 匹配两位及两位以上数字 |
\{n,m\} | 匹配前面的子表达式n到m次,例 : go\{2,3\}d 、[0-9]\{2,3\} 匹配两位到三位数字 |
注: egrep 、 awk 使用{n} 、{n,} 、{n,m} 匹配时 {} 前不用加 \ | |
\w | 匹配包括下划线的任何单词字符。 |
\W | 匹配任何非单词字符。等价于[^A-Za-z0-9_] 。 |
\d | 匹配一个数字字符。 |
\D | 匹配一个非数字字符。等价于[^0-9] 。 |
\s | 空白符。 |
\S | 非空白符。 |
扩展正则表达式
支持的工具:egrep
、awk
,注意:使用grep
要配合-E
或者-P
使用,sed
要配合-r
使用。
五、shall实战
打印99乘法表
#!/bin/bash
for i in {1..9};dofor j in `seq $i`;doecho -e "${j}x$i=$((j*i))\t\c "doneecho
done
结果
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
方法2
#!/bin/bash
for((i=1;i<10;i++))
dofor ((j=1;j<=i;j++)) ;doecho -e "$j×$i=$((j*i))\t\c"done
echo
done
输出结果一样
直角三角形
[root@localhost ~]# bash 99.sh
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
[root@localhost ~]# cat 99.sh
#!/bin/bash
for((i=1;i<10;i++))
dofor ((j=1;j<=i;j++)) ;doecho -e "*\t\c"done
echo
done
创建用户
read -p "请输入你创建的用户名:" name
test -d /home/$name
if [ $? -eq 0 ];
thenecho "用户已存在"exit 0;
elseecho "正在创建用户..."useradd $nameecho 123.com | passwd --stdin $nameecho "已经创建用户$name,初始密码为123.com"exit 1;
fi
猜数游戏
#!/bin/bash
# 脚本生成一个100以内得随机数,提示用户猜数字,根据用户得输入,提示用户猜小了或猜大了,直至用户猜对脚本结束
# RANDOM为系统自带的系统变量,值为0~32767的随机数
# 使用取余算法将随机数变为1~100的随机数
num=$[RANDOM%100+1]
echo $num# 使用read 提示用户猜数字
# 使用if判断用户猜数字的大小关系:
# -eq(等于),-ne(不等于),-gt(大于),-ge(大于等于),-lt(小于),-le(小于等于)
while :
do read -p "数字炸弹在1~100的随机数,请选择数字:" nif [ $n -eq $num ];thenecho "恭喜,爆炸了"exitelif [ $n -gt $num ];thenecho "你猜大了,缩小范围为:0~$n之中"elseecho "你猜小了,缩小范围为$n~100之中"fi
done