shell程序设计语言
1、什么是shell
1、一层指的是shell这门语言,是一种特定的语法风格,规范等
2、另外一层指的是专门用于解释执行shell这门语言语法的应用程序,即shell解释器,我们常用的是bash
3、CentOs自带了Shell解释器,不需要额外安装。在xshell中输入如下命令就可以查看默认的解释器在哪
cat /etc/passwd
2、Shell的优点
- 自动备份
- 自动部署
- 监控脚本
- 自动运行任务
3、Shell脚本的编写
我们编写Shell脚本的地方有两处:第一处是在交互式环境中,也就是我们的linux窗口;第二处就是写入到文件去,注意这里的文件通常以.sh结尾
步骤:
1、打开我们的xshell连接到虚拟机中,进入到soft目录下,创建一个Shelldirs目录,以后我们编写shell脚本的文件都可以放在此处
2、在Shelldirs目录下创建一个study_shell的目录存放我们学习shell程序的文件,并在此文件下创建一个文件study_demo.sh,并在里面写入符合shell程序的代码即可。
3、将文件保存后,在交互界面输入 sh study_demo.sh,即可执行shell程序。
4、这样就完成了我们的第一个shell程序。
注意事项:
1、在study_demo.sh文件中第一行表示我们选择使用的shell解释器是bash,也可以用:#!/usr/bin/env bash。shell的第一行比较特殊,一般都会以#!开始来指定使用的shell解释的类型。在linux中,除了bash shell以外,还有很多版本的shell, 例如zsh、dash等等,不过bash shell是使用最多的。
2、第二行以#号开始,表示本行是注释,注释是对代码的解释说明,注释的内容不会执行
4、Shell脚本程序运行的三种方式
- rw- r-- r--. 1 root root 96 1月 23 15:53 a1.txt
文件属性 所属用户权限 所属用户组权限 其他用户权限 归属用户 归属用户组 字节大小
-: 普通文件或者压缩包 r: 读取权限 4
d: 文件夹/目录 w: 写数据权限 2
l: 软链接 x: 执行权限 1
# 方法一:bash或sh命令来执行
sh 文件名/文件所在的路径
bash 文件名/文件所在的路径
# 注意:当脚本没有X权限时,root和文件所有者通过该方式也可以正常执行
# 方法二:输入shell脚本的绝对路径或相对路径
# r(读权限):4 w(写权限):2 x(执行权限):1
# 解释:读权限所对应的数字是4,写权限所对应的数字是2,执行权限所对应的数字是1。如果要给一个文件赋予读写执行的权限,由于这三个权限所对应的数字是4,2,1,加在一起是7,故权限的赋予可以这么写:
chmod 777 文件
# 第一个7代表的是所属用户的权限有读写和可执行,第二个7代表的是所属用户组权限,最后一个7代表的是其他用户权限。
# 需要注意的是,使用此方法前需要给文件赋予可执行的权限,也可以使用如下命令:
chmod a+x xxx.sh
# 方法三:在shell脚本的路径前加source命令
source xxx.sh
4.1 三种方式的区别
第一种方法和第二种方法会新开一个bash,不同bash中的变量无法共享。
第三种方法是在同一个shell里面执行的,不会重新开辟一个bash
简单来说就是用第一种方法和第二种方法时,在交互界面定义一个变量a=100,这是一个进程,当我执行study_demo1.sh时这又是一个进程。所以说一个shell环境就是一个单独的全局作用域,不同的shell环境,无法访问彼此shell环境中的变量。
但是第三种方法则不会出现上述情况,由于它是作用在同一个shell环境执行。
解决方案:export :可以将当前进程的变量传递给子进程去使用
注意:将来配置profile的时候所有的变量前必须加export
5、shell的注释方法
方法一:
#
方法二(多行注释):
# 此处的EOF可以是任意字符,但是一定要注意最后结尾一定要用相同的字符结尾
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
:<<!
注释内容...
注释内容...
注释内容...
!
6、变量的定义,删除,传值与应用
6.1 变量的定义
# 1、语法:变量名=值
# 2、注意:等号左右两边不能有空格!!!
[root@master2024 study_shell]# name="xiaoyu"
[root@master2024 study_shell]# echo ${name}
6.2 变量的删除
[root@master2024 study_shell]# unset name
6.3 变量的传值(shell脚本传值和用户交互传值)
shell脚本的传值:
[root@master2024 study_shell]# echo study_demo2.sh 11 22 33 44 55 66 77 88 99 100 110 120 study_demo2.sh 11 22 33 44 55 66 77 88 99 100 110 120
注意事项:
1、从调用脚本时传入的位置参数获取变量值时需要用到 n 获取第 n 个位置参数值,超过 10 需要用 n获取第n个位置参数值,超过10需要用 n获取第n个位置参数值,超过10需要用{n},如下$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
2、其中$0代表的是文件名
用户交互传值:
# 一:read接收用户的输入,即从键盘读入变量值 read -p "提示信息: " 变量名 # 在.sh文件中写入 #! /bin/hash read -p "请您输入姓名:" name echo ${name}
# 其他的一些传参注意事项 # 显示转义字符 echo "\"Hello World\"" # 显示换行 echo -e "OK! \n" echo "Hello World" # 显示不换行 echo -e "OK! \c" echo "Hello World" # 显示结果定向至文件 echo "Hello World" > myfile ## > 代表覆盖 # >> 追加写入 # 还可以输出带颜色(了解即可) # echo -e "\033[字背景颜色;文字颜色m字符串\033[0m" # 例如 # echo -e "\033[47;30m I love shujia! \033[0m",其中47的位置代表背景色, 30的位置是代表字体颜色 echo -e "\033[31m 红色字 \033[0m" echo -e "\033[34m 黄色字 \033[0m" echo -e "\033[41;33m 红底黄字 \033[0m" echo -e "\033[41;37m 红底白字 \033[0m" # 需要使用参数-e,man echo 可以知道-e:enable interpretation of backslash escapes。
6.4 变量传值的案例
获取当前的系统时间
在shell脚本文件中编写的代码如下:
#! /bin/bash echo "当前的时间为:" echo "${1} ${2} ${3} 星期:${4} 时间:${5} 时区:${6}" # 传递时间参数 [root@master2024 study_shell]# sh study_demo4.sh `date`
结果:
7、预订变量
参数处理 | 参数说明 |
---|---|
$# | 传递到脚本的参数个数 |
∗ / */ ∗/@ | 以一个单字符串显示所有向脚本传递的参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
$0 | 执行的文件名 |
$* 所有的参数
$@ 所有的参数
对于后面说的for循环而言,上面两个没有区别
$# 参数的个数
$$ 当前进程的PID # 此外,可以使用只读变量来获取父进程的PID:$PPID、获取执行脚本的用户ID:$UID
$? 上一个命令的返回值 0表示成功
8、数组
数组分为两种
• 普通数组:只能使用整数作为数组索引
• 关联数组:可以使用字符串作为数组索引,需要用declare -A声明
8.1 普通数组
# 普通数组的声明
# 方式一:array=(元素1 元素2 元素3)
array=(xiaohu 18 male)
# 注意在shell中数组中的元素需要用空格分隔而不是用逗号,而且用的是小括号。
# 方式二:array=([key1]=value1 [key2]=value2 [key3]=value3)
array=([1]=111 [0]="two" [2]=333)
# 数组中索引为0的元素为two,索引为1的元素为111,索引为2的元素为333
array=([0]=111 [3]="two" [5]=333)
# 数组中索引为0的元素为111,索引为1的元素为空,索引为2的元素为空,索引为3的元素为two,索引为4的元素为空,索引为5的元素为333
# 方式三:依次赋值
array_new[0]=111
array_new[1]=222
array_new[2]="third"
# 方式四:利用执行命令的结果设置数组元素:array=($(命令)) 或者 array=(`命令`)
该方式会将命令的结果以空格为分隔符切成多个元素然后赋值给数组
[root@master2024 study_shell]# ls
study_demo1.sh study_demo2.sh study_demo3.sh study_demo4.sh study_demo.sh
[root@master2024 study_shell]# array1=(`ls`)
# 查看普通数组
[root@master2024 study_shell]# declare -a |grep array1
declare -a array1='([0]="study_demo1.sh" [1]="study_demo2.sh" [2]="study_demo3.sh" [3]="study_demo4.sh" [4]="study_demo.sh")'
# ps:查看声明过的普通数组
declare -a
# 访问普通数组
[root@master soft]# ip_array=(1.1.1.1 2.2.2.2 3.3.3.3)
# 正向索引
[root@master soft]# echo ${ip_array[0]}
1.1.1.1
[root@master soft]# echo ${ip_array[1]}
2.2.2.2
[root@master soft]# echo ${ip_array[2]}
3.3.3.3
[root@master soft]#
# 负向索引
[root@master soft]# echo ${ip_array[-1]}
3.3.3.3
[root@master soft]# echo ${ip_array[-2]}
2.2.2.2
[root@master soft]# echo ${ip_array[-3]}
1.1.1.1
# 查看全部元素
[root@master soft]# echo ${ip_array[*]}
8.2 关联数组
对关联数组赋值之前,必须要先声明一下关联数组,才能进行赋值!!!
# 声明关联数组
[root@master2024 study_shell]# declare -A info
# 对关联数组进行赋值
# 方法一
[root@master2024 study_shell]# info["name"]="小宇"
[root@master2024 study_shell]# info["age"]=18
[root@master2024 study_shell]# info["address"]="安徽合肥"
# 方法二
[root@master2024 study_shell]# info=(["name"]="小宇" ["age"]=18 ["address"]="安徽合肥")
# 查看关联数组
[root@master2024 study_shell]# declare -A |grep info
declare -A info='([name]="小宇" [age]="18" [address]="安徽合肥" )'
8.3 普通数组与关联数组的区别
1、关联数组可以用任意的文本作为数组索引,而普通数组中的索引都是整数。
2、关联数组使用之前需要声明: declare -A 数组名
9、变量值的相关操作
9.1获取变量值的长度
[root@master2024 study_shell]# info="戴浩冉长得天下第一帅"
[root@master2024 study_shell]# echo ${#info}
10
9.2 切片
[root@master soft]# msg="abcdef"
[root@master soft]# echo ${msg:3} # 从3号索引开始,一直到最后
def
[root@master soft]# echo ${msg:3:2} # 从3号索引开始,往后数2个字符
de
[root@master soft]# echo ${msg::3} # 从0开始,往后数3个字符
abc
9.3 截断
左边截断:
[root@master2024 study_shell]# url="www.baidu.com" [root@master2024 study_shell]# echo ${url#www.} baidu.com # 结合 #* 非贪婪,默认情况下*是非贪婪,尽可能地少“吃”字符 [root@master2024 study_shell]# echo ${url#*w} ww.baidu.com # 结合 ##* 贪婪,尽可能地多“吃”字符 # *会尽可能多地吃掉字符,一直匹配到最远的那个w才停下来 [root@master2024 study_shell]# echo ${url##*w} .baidu.com
右边截断:
# 1.1 简单使用 [root@master2024 study_shell]# echo ${url%.com} www.baidu # 1.2 结合%.*非贪婪 [root@master2024 study_shell]# echo ${url%.*} www.baidu # 1.3 结合%%.*贪婪 [root@master2024 study_shell]# echo ${url%%.*} www
区别:
1、左截断时,贪婪匹配的*与#相连,即#*后面加截断的字符,##*后面加截断的字符 2、右截断时,贪婪匹配的*与%相隔,即%后面加截断的字符再加上*,%%后面加截断的字符再加上*
9.4 let操作
[root@master soft]# j=1
[root@master soft]# let ++j
[root@master soft]# echo $j
2
# 在使用i++,++i,--i,i--等等都需要用到let
10、运算符
以本人来看需要掌握浮点运算bc,和整数运算let即可,因为bc可以包含整数运算。
bc计算工具(yum install bc -y)
# 使用bc的格式
res=`echo "运算表达式" | bc`
echo res
[root@master soft]# res=`echo "1+1" | bc`
[root@master soft]# echo $res
2
[root@master soft]# res=`echo "10%3" | bc`
[root@master soft]# echo $res
1
[root@master soft]# res=`echo "1.2+1.3" | bc`
[root@master soft]# echo $res
2.5
[root@master soft]# res=`echo "5.0+3.0" | bc`
[root@master soft]# echo $res
8.0
11、文件测试运算符
# 判断文件是否可读
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
# 判断文件是否可写
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
# 判断文件是否为可执行文件
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
# 判断是否为特殊文件
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
# 判断是否为目录
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
# 判断文件是否为空
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
12、字符串测试运算符
# -z 判断字符串长度是否为0
[root@master soft]# ip=""
[root@master soft]# [ -z "$ip" ];echo $? # 注意引号
0
# -n 判断字符串长度是否不为0
[root@master soft]# [ -n "$ip" ];echo $? # 注意加引号
1
# 0代表不为空,1代表为空
13、数值测试符(比较运算符)
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回true | [$a -eq $b ]返回 false。 |
-ne | 检测两个数是否不相等,不相等返回true | [$a -ne $b ]返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,返回true | [$a -gt $b ]返回 false. |
-lt | 检测左边的数是否小于右边的,如果是,返回true | [$a -It $b ]返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,返回true | [$a -ge $b ]返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,返回true | [$a -le $b ]返回 true. |
14、关系运算符
需要结合(())进行使用
[root@master soft]# x=100
[root@master soft]# (($x>10))
[root@master soft]# echo $?
0
[root@master soft]# (($x < 10));echo $?
1
[root@master soft]# (($x == 100));echo $?
0
[root@master soft]# (($x != 100));echo $?
1
[root@master soft]# (($x != 100)) && (("xiaohu" == "xiaohu"))
[root@master soft]# echo $?
1
[root@master soft]# (($x != 100)) || (("xiaohu" == "xiaohu"))
[root@master soft]# echo $?
0
[root@master soft]# (($x != 100 && "xiaohu" == "xiaohu"));echo $?
1
[root@master soft]# (($x != 100 || "xiaohu" == "xiaohu"));echo $?
0
15、赋值运算符
[root@master soft]# x=10
[root@master soft]# ((x%3))
[root@master soft]# echo $x
10
[root@master soft]# ((x%=3))
[root@master soft]# echo $x
1
# 相关括号:
格式1: test 条件表达式
格式2: [ 条件表达式 ] -eq -lt -gt -le -ge -ne
格式3: (()) 数值比较,运算 += -= %= == != < > <= >= && ||
格式4: [[ 条件表达式 ]],支持正则 =~
# 双中括号的正则
[root@master soft]# [[ "$USER" == "root" ]];echo $? # 注意内层[]中包含的内容必须左右两侧加空格
0
[root@master soft]# [[ "$USER" == "root" ]];echo $? # 一个等号也行两个等号也可以额
0
# 此外[[]]内部是可以使用正则的,注意:正则表达式不要加引号
[root@master soft]# [[ "$USER" =~ ^wyh$ ]];echo $? # 正则表达式不要加引号
1
[root@master soft]# [[ "$USER" =~ ^root$ ]];echo $? # 正则表达式不要加引号
0
[root@master soft]# [[ ! "$USER" =~ t$ ]] && echo 此时不是管理员登录 || echo 是管理员登录
是管理员登录
[root@master soft]# num1=123
[root@master soft]# [[ "$num1" =~ ^[0-9]+$ ]];echo $? # 判断是否是数字
0
[root@master soft]# [[ "$num1" =~ ^[0-9]+$ ]] && echo "是数字"
是数字
[root@master soft]# num2=abc123de
[root@master soft]# [[ "$num2" =~ ^[0-9]+$ ]];echo $?
1
[root@master soft]# [[ "$num2" =~ ^[0-9]+$ ]] || echo "num2不是数字"
num2不是数字
16、布尔运算符
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为true则返回false,否则退回true。 | [ ! false ]返回 true。 |
-o | 或运算,有一个表达式为true则返回true。 | [ $a -It 20 -o $b -gt 100 ]返回 true。 |
-a | 与运算,两个表达式都为true才返回true. | [ $a -It 20 -a $b -gt 100 J 返回 false。 |
17、逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的AND | [[$a -It 100 && $b-gt 100 ]]返回 false |
|| | 逻辑的OR | [[$a -It 100 || $b -gt 100 ]]返回 true |
18、流程控制
18.1 if语句
# 单分支
if 条件
then
要执行的命令1
要执行的命令2
要执行的命令3
...
fi
# 双分支
if 条件
then
要执行的命令1
要执行的命令2
要执行的命令3
...
else
要执行的命令1
要执行的命令2
要执行的命令3
...
fi
# 多分支
if 条件;then
要执行的命令1
要执行的命令2
要执行的命令3
...
elif 条件;then
要执行的命令1
要执行的命令2
要执行的命令3
...
elif 条件;then
要执行的命令1
要执行的命令2
要执行的命令3
...
...
else
要执行的命令1
要执行的命令2
要执行的命令3
...
fi
举例:
======================版本1====================== #!/bin/bash age=87 read -p 'num: ' n if [ $n -eq $age ];then echo 'you get it' elif [ $n -gt $age ];then echo 'too big' elif [ $n -lt $age ];then echo 'too small' fi ======================版本2====================== #!/bin/bash read -p ">>> " num [[ ! $num =~ ^[0-9]+$ ]] && echo "请输入数字" && exit if [ $num -gt 18 ];then echo "too big" elif [ $num -lt 18 ];then echo "too small" else echo "you got it" fi
18.2 case选择
case 变量 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
模式3)
命令序列3
;;
*)
无匹配后命令序列
esac
注意:
case语句只支持shell通配符,例如:*表示任意字符串,?表示任意字符,中括号表示字符集如[a-z]表示一个小写字母
如果要处理正则表达式则需要使用用if [[ 字符串 =~ “正则” ]]这种形式():用来表示数组
(()):用来描述数值测试符,>=,==,!=,等等
案例:
#!/bin/bash
read -p "username: " -t 5 username
echo
if [ -z $username ]
then
username="default"
fi
case $username in
root)
echo "管理员用户"
;;
xiaohu)
echo "普通用户"
;;
default)
echo "默认用户"
;;
*)
echo "其他用户"
esac
18.3 while循环
# 一、while语句结构:条件为真时,执行循环体代码
while 条件
do
循环体
done
# 二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do
循环体
done
案例:
[root@master soft]# cat a.sh #!/bin/bash x=0 while (($x<3)) do echo $x # 0,1,2 let x++ done echo "================" y=0 until (($y==3)) do echo $y # 0,1,2 let y++ done [root@master soft]# ./a.sh 0 1 2 ================ 0 1 2
流程控制语句:
continue:默认退出本次循环
break:默认退出本层循环
18.4 for循环
# Shell风格语法
for 变量名 [ in 取值列表 ]
do
循环体
done
# C语言风格语法
for ((初值;条件;步长))
do
循环体
done
案例1 shell风格
for i in {1..10} do echo $i done
案例2:C语言风格
for ((i=1;${i}<=10;i++)) do echo $i done
18.5 扩展使用select
select var in ...
do
...
break
done
案例:
[root@master soft]# cat select2.sh #!/bin/bash PS3='choose one: ' # select默认使用PS3变量的值做提示符 echo select var in $1 $2 $3 $4 do echo echo "your choose is $var" echo "OK" echo break # 跳出select,否则是死循环 done [root@master soft]# [root@master soft]# ./select2.sh 苹果 梨 蔬菜 香蕉 1) 苹果 2) 梨 3) 蔬菜 4) 香蕉 5) 茄子 choose one: 1 your choose is 苹果 OK
19、函数
#语法:
[ function ] funname [()]
{
命令1;
命令2;
命令3;
...
[return int;]
}
# 示例1:完整写法
function 函数名() {
函数要实现的功能代码
}
# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {
函数要实现的功能代码
}
# 示例3:省略关键字function
函数名() {
函数要实现的功能代码
}
)
do
echo $i
done
18.5 扩展使用select
select var in ...
do
...
break
done
案例:
[root@master soft]# cat select2.sh #!/bin/bash PS3='choose one: ' # select默认使用PS3变量的值做提示符 echo select var in $1 $2 $3 $4 do echo echo "your choose is $var" echo "OK" echo break # 跳出select,否则是死循环 done [root@master soft]# [root@master soft]# ./select2.sh 苹果 梨 蔬菜 香蕉 1) 苹果 2) 梨 3) 蔬菜 4) 香蕉 5) 茄子 choose one: 1 your choose is 苹果 OK
19、函数
#语法:
[ function ] funname [()]
{
命令1;
命令2;
命令3;
...
[return int;]
}
# 示例1:完整写法
function 函数名() {
函数要实现的功能代码
}
# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {
函数要实现的功能代码
}
# 示例3:省略关键字function
函数名() {
函数要实现的功能代码
}