Shell脚本编程:是基于过程式,解释执行的语言
编程语言的基本结构:
shell脚本:包含一些命令或声明,并符合一定格式的文本文件
格式要求:首行执行shebang机制
#声明后续语句是通过那种语言写的
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/bin/bash
[root@localhost ~]# chmod +x shellScript/hello.sh
[root@localhost ~]# /root/shellScript/hello.sh
位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数
$1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个
#预定义变量
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合成一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
$? 上个命令的退出状态,或函数的返回值
$$ 当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID
注意:$@,$*只有被双引号括起来的时候才会有差异
字符串(String)就是一系列字符的组合。字符串是Shell编程中最常用的数据类型之一
字符串可以由单引号''
包围,也可以由""
包围,也可以不用引号,三种方式的区别
' '
包围的字符串
" "
包围的字符串
""
包围的字符串一样在Shell中获取字符串长度很简单,具体方法如下:
${#string_name}
string_name:表示字符串名字
在脚本语言中,字符串的拼接(也称为字符串连接或者字符串合并)往往都非常简单,在Shell中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接
Shell截取字符串通常有两种方式,从指定位置开始截取和从指定字符(子字符串)开始截取
从指定位置开始截取
这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串
既然需要指定起始位置,那么就要涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数?答案是:Shell同时支持两种计数方式
1.从字符串左边开始计数
如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:
${string:start:length}
其中,Sting是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)
例如:
url="c.biancheng.net"
echo ${url:2:9}
>结果为:biancheng
url="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
%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
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,实现评估布尔声明,以便在条件性环境下进行执行。
语法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 ~]#
常用的文件测试操作符 | 说明 |
---|---|
-a/-e 文件 | 文件是否存在 |
-d 文件 | 文件存在且为目录则为真,即测试表达式成立 |
-f 文件 | 文件存在且为普通文件则为真,即测试表达式成立 |
-r 文件 | 文件存在且可读为真 |
-w 文件 | 文件存在且可写为真 |
-x 文件 | 文件存在且可执行则为真 |
-z字符串若字符串的长度为0,则为真,z可以理解为zero
“字符串1” == ”字符串2“若字符串1长度等于字符串2长度,则为真
“字符串1” != ”字符串2“若字符串1长度不等于字符串2长度,则为真
“字符串1” =~ “字符串2”左侧字符串是否能被右侧的PATTERN所匹配。
在[ ] 或 test中使用的比较符号 | 在(()) 或 [[ ]]中使用的比较符号(不用这个做数字比较) | 说明 |
---|---|---|
-eq | == 或 = | 相等,equal |
-ne | != | 不相等,not equal |
-gt | > | 大于,greater than |
-ge | > = | 大于等于,greater equal |
-lt | < | 小于,less than |
-le | < = | 小于等于,less equal |
( )和 { }都可以将多个命令组合再一次,批量执行,{ } 里的内容需要与两侧用空格隔开并在命令结尾加上;
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作为输入源,而不是标准输入,类似于重定向 |
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]#
if [ 条件判断式 ];then
命令
fi
或者
if [ 条件判断式 ]
then
命令
fi
if [ 条件判断式 ]
then
命令
else
命令
fi
if [ 条件判断式1 ]
then
命令
elif [ 条件判断式2 ]
then
命令
...
...
else
命令
fi
说明:
格式
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
备份成功
将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
常见的循环的命令:for,while
#循环的逻辑:程序先进行语句判断,如果为真则执行循环语句,然后再进行语句判断,直至语句判断失败才跳出
格式:
第一种写法
for NAME [in words ...]; do commands;done
# 第二种写法
for 变量 in 列表
循环体
done
# 第三种写法
for 变量 in 列表
do
循环体
done
while command; do commands;done
while condition;do 循环体 done
无限循环
while true;do
循环体
done
continue\[N\]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
格式:
while CONDITION1;do
循环体1
...
if command2;then
continue
fi
CMDn
....
done
break\[N\]:提前结束第N层后的全部循环;最内层为第1层,默认为1
#!/bin/bash
for((i=0;i<10;i++));do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && break
echo $j
done
echo ----------------------------
done
数组是若干数据的集合,其中存放的每一份数据都称为元素。Shell
不限制数组的大小,理论上可以存放无限量的数据,Shell
数组元素的下标也是从0开始计数
获取数组中的元素要使用下标[ ],下标可以是一个整数,也可以是一个结果为整数的表达式;下标必须大于等于0
注意:
Shell
只支持一维数组,不支持多维数组
在Shell
中,用小括号()
来表示数组,数组元素之间用空格来分隔
#arrayname=(1 2 3 4 5)
`输出定义数组中的全部元素
#echo ${arrayname[*]}
#echo ${arrayname[@]}
`输出定义数组中的第二个元素
#echo ${arrayname[0]}
`输出定义数组中的第二个元素
#echo ${arrayname[1]}
`输出定义数组中的元素个数
#echo ${#arrayname[*]}
array_new=(${array1[@]} ${array2[@]})
array_new=(${array1[*]} ${array2[*]})
`两种方式是等价的,选择其一即可。其中,array1 和 array2 是需要拼接的数组,array_new 是拼接后形成的新数组。
unset array_name[index]
`其中,array_name表示数组名,index表示数组下标
unset array_name
`删除整个数组
在Shell
中直接通过${数组名[@/*]:起始位置:长度}
获取数组给定范围内元素,返回字符串,中间用空格分开
#!/bin/bash
array=(yoona lucy tom)
echo ${array[*]}
echo ${array[*]:1:2}
echo ${array[@]:0:2}
-->结果为:
yoona lucy tom
lucy tom
yoona lucy
Shell函数的本质是一段可以重复使用的脚本代码,这段代码被提前编好了,放在了指定位置,使用时直接调用即可
Shell 中的函数和C++、Java、Python、C# 等其它编程语言中的函数类似,只是在语法细节有所差别。
Shell 函数定义的语法格式如下:
function name() {
statements
[return value]
}
对各个部分的说明:
function
是 Shell 中的关键字,专门用来定义函数;name
是函数名;statements
是函数要执行的代码,也就是一组语句;return value
表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。由{ }
包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
正则表达式通常用于判断语句中,用来检查某一字符串是否满足某一格式。
正则表达式是由普通字符与元字符组成。普通字符包括小写字母、数字、标点符号及一些其他符号。元字符是指在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
正则表达式根据从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
使用。
#!/bin/bash
for i in {1..9};do
for j in `seq $i`;do
echo -e "${j}x$i=$((j*i))\t\c "
done
echo
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++))
do
for ((j=1;j<=i;j++)) ;
do
echo -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++))
do
for ((j=1;j<=i;j++)) ;
do
echo -e "*\t\c"
done
echo
done
read -p "请输入你创建的用户名:" name
test -d /home/$name
if [ $? -eq 0 ];
then
echo "用户已存在"
exit 0;
else
echo "正在创建用户..."
useradd $name
echo 123.com | passwd --stdin $name
echo "已经创建用户$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的随机数,请选择数字:" n
if [ $n -eq $num ];then
echo "恭喜,爆炸了"
exit
elif [ $n -gt $num ];then
echo "你猜大了,缩小范围为:0~$n之中"
else
echo "你猜小了,缩小范围为$n~100之中"
fi
done