Shell程序设计语言
一、认识Shell
1.1 编程语言的种类
# 机器语言:站在计算机(奴隶)的角度,说计算机能听懂的语言,那就是直接用二进制编程,直接操作硬件;优点:执行效率最高缺点:1、二进制指令难以记忆,开发时极容易出错2、开发程序的复杂度高:即便是完成一个简单的功能,需要用到的二进制指令的条数都会非常多
# 汇编语言:站在计算机(奴隶)的角度,简写的英文标识符取代二进制指令去编写程序,本质仍然是直接操作硬件;优点:解决了机器语言的二进制指令难以记忆的问题,执行效率还是高缺点:开发程序的复杂度依然很高:因为汇编语言就是用英文标签对应原来的二进制指令,好记归好记,开发的复杂度却没有降低ps:因为上述两类语言都是在直接与计算机硬件打交道,离计算机硬件比较近,所以又统称为低级语言
# 高级语言:站在人(奴隶主)的角度,说人话,即用人类的字符去编写程序,屏蔽了硬件操作优点:开发复杂度地,即开发效率高缺点:速度肯定是不如低级语言,一直到今天,对速度要求极高的场景还会用到低级语言,比如操作系统的调度程序
1.2 什么是Shell
# 1、一层指的是shell这门语言,是一种特定的语法风格,规范等# 2、另外一层指的是专门用于解释执行shell这门语言语法的应用程序,即shell解释器,我们常用的是bash
我们centOS7自带了Shell解释器,不需要额外安装
一般情况下,我们创建的用户默认是/bin/bash
shell 本身就是一门解释型、弱类型、动态语言,与 python 相对应, Python 属于解释型、强类型、动态语言,我们平时登录成功一个用户后进入的就是 bash 解释器的交互式环境,我们敲的命令其实都属于 shell 这门语言的语法
shell的优点
1. 自动备份
2. 自动部署
3. 监控脚本
4. 自动运行任务
1.3 第一个shell脚本
其实我们写shell脚本有两个地方可以写:1. 交互式环境(简单理解就是我们的linux窗口)2. 写到文件中(通常以.sh结尾)
交互式环境写脚本
脚本文件
其实将我们在交互式环境里敲的命令直接放到一个文本文件里,一个简单的 shell 程序就完成了 注意:shell 解释器执行程序是解释执行的,即打开文件内容,因此文件的后缀名没有硬性限制,但通常定义为.sh 结尾
解释:
# 第一行表示我们选择使用的shell解释器是bash,也可以用:#!/usr/bin/env bashshell的第一行比较特殊,一般都会以#!开始来指定使用的shell解释的类型。在linux中,除了bash shell以外,还有很多版本的shell, 例如zsh、dash等等...不过bash shell还是我们使用最多的。
# 第二行以#号开始,表示本行是注释,注释是对代码的解释说明,注释的内容不会执行,对关键代码加注释是一种好的编程习惯
# 第三行中的echo是linux中的输出命令,该行的意思很明显的就是输出hello world!
1.4 shell脚本的运行方式
方式一:输入脚本的绝对路径或相对路径
/root/shujia.sh./shujia.sh# 注意:执行的必须是一个可执行文件 chmod a+x xxx.sh
方式二:bash或sh +脚本
sh helloworld.sh# 注意:当脚本没有X权限时,root和文件所有者通过该方式可以正常执行
方式三:在脚本的路径前再加"或source
source helloworld.sh
区别:
第一种和第二种会新开一个bash,不同bash中的变量无法共享。第三种是在同一个shell里面执行的# 一个shell环境就是一个单独的全局作用域,不同的shell环境,无法访问彼此shell环境中的变量
[root@master ~]$ x=99
[root@master ~]$ cat /a/b/hello.sh
#!/bin/bashecho "hello world!"
echo $x # 我们在这里访问一下全局变量x[root@master ~]$ source /a/b/hello.sh # 在当前shell环境执行,可以访问到x
hello world!
111 # 取到了x的值
[root@master ~]$ bash /a/b/hello.sh # 在子shell环境执行,不能访问到x
hello world!# 此处打印空
解决方案:export :可以将当前进程的变量传递给子进程去使用
注意:将来配置profile的时候所有的变量前必须加export
二、Shell编程语法
2.1 注释
可以加在被注释代码的正上方单独一行,或者被注释代码的正后方,例如
echo "hello" # 注释。。。
不用全部加注释,只需要在自己觉得重要或不好理解的部分加注释即可
注释可以用中文或英文,但不要用拼音
分类:
单行注释:#
以 # 开头的行就是注释,会被解释器忽略。通过每一行加一个 # 号设置多行注释
多行注释 :<<
# 特殊的多行注释
# end of file
:<<EOF
注释内容...
注释内容...
注释内容...
EOF:<<!
注释内容...
注释内容...
注释内容...
!
2.2 变量
借助java程序对变量的理解:变量就是程序运行过程中其值可以发生改变的量
语法定义格式
# 1、语法:变量名=值
# 2、注意:等号左右两边不能有空格!!!
# 3、例如:
[root@master soft]# name="xiaohu"
变量的引用
[root@master soft]# address="安徽合肥"
[root@master soft]# echo $address
安徽合肥注意:如果是打印百分比,建议使用${变量名}%
[root@master soft]# percent=90
[root@master soft]# echo ${percent}%
90%
删除变量
[root@master soft]# x=999
[root@master soft]# unset x
[root@master soft]# echo $x[root@master soft]#
变量的命名规范
# 变量名的命令应该见名知意,同时遵循如下规则
以字母或下划线开头,剩下的部分可以是:字母、数字、下划线,最好遵循下述规范:1.以字母开头2.使用中划线或者下划线做单词的连接 shujia-xiaohu shujia_xiaohu 3.同类型的用数字区分 shujia1 shujia24.对于文件名的命名最好在末尾加上拓展名 score_xiaohu.sh score_shizhaoyang.sh例如: shujia_test.tar.gz 5、不要带有空格、?、*等特殊字符6、不能使用bash中的关键字,例如if,for,while,do等7、不要和系统环境变量冲突
变量的使用场景
场景一:直接赋值
# 1. 显式赋值:变量名=变量值
[root@master soft]# name2="xiaohu"
[root@master soft]# echo $name2
xiaohu
[root@master soft]# name2="xiaohua"
[root@master soft]# echo $name2
xiaohua
场景二:脚本运行传值
从调用脚本时传入的位置参数获取变量值:./a1.sh a1 a2 a3
需要用到$n获取第n个位置参数值,超过10需要用${n},如下
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}# 示例
[root@master soft]# cat a1.sh
#!/bin/bash
echo ${0}
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8
echo $9
echo ${10}
echo ${11}
echo ${12}
场景三:用户交互传值
何为交互,即输入、输出# 一:read接收用户的输入,即从键盘读入变量值
read 变量名
read -p "提示信息: " 变量名
read -t 5 -p "提示信息: " 变量名 # -t指定秒数
read -n 2 变量名 # -n读取的字符个数应用示例:vim name.sh
read -p "请输入你的姓名 " name
echo $name# 二:输出
# echo命令
[root@master soft]# name="xiaohu"
[root@master soft]# age=18
[root@master soft]# echo -e "my name is\t$name\nmy age is\t$age"
my name is xiaohu
my age is 19还可以输出带颜色(了解即可)
# 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。 ## 显示普通字符串
echo "Hello World" ## 显示转义字符
echo "\"Hello World\"" ## 显示变量
name="zhangsan"
echo "$name Hello World" ## 显示换行
echo -e "OK! \n"
echo "Hello World" ## 显示不换行
echo -e "OK! \c"
echo "Hello World" ## 显示结果定向至文件
echo "Hello World" > myfile
## > 代表覆盖
# >> 追加写入## 原样输出字符串
echo '$name\"' ## 显示命令执行结果
echo `date`
预定变量
参数处理 | 参数说明 |
---|---|
$# | 传递到脚本的参数个数 |
$*/$@ | 以一个单字符串显示所有向脚本传递的参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
$0 | 执行的文件名 |
$* 所有的参数
$@ 所有的参数
对于后面说的for循环而言,上面两个没有区别$# 参数的个数
$$ 当前进程的PID # 此外,可以使用只读变量来获取父进程的PID:$PPID、获取执行脚本的用户ID:$UID
$? 上一个命令的返回值 0表示成功 示例1:
[root@master soft]# chmod +x b.sh
[root@master soft]# ./b.sh a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
5
18988
1
[root@master soft]# cat b.sh
#!/bin/bash
echo $*
echo $@
echo $#
echo $$
2.3 常量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。[root@master soft]# x=999
[root@master soft]# readonly x
[root@master soft]# x=111
-bash: x: 只读变量
如何删除readonly常量?
下载gdb
[root@master soft]# yum install gdb -y
删除
[root@master soft]# readonly x=123
[root@master soft]# cat << EOF | gdb
> attach $$
> call unbind_variable("x")
> detach
> EOF
2.4 数据类型
shell是一门解释型,弱类型,动态语言
概括地说,编程语言的划分方式有以下三种# 1、编译型or解释型# 2、强类型or弱类型
2.1 强类型语言: 数据类型不可以被忽略的语言
即变量的数据类型一旦被定义,那就不会再改变,除非进行强转。
在python中,例如:name = 'xiaohu',这个变量name在被赋值的那一刻,数据类型就被确定死了,是字符型,值为'xiaohu'。2.2 弱类型语言:数据类型可以被忽略的语言
比如linux中的shell中定义一个变量,是随着调用方式的不同,数据类型可随意切换的那种,即shell对数据类型的概念没有那么强的要求# 3、动态型or静态型
3.1 动态语言 :运行时才进行数据类型检查
即在变量赋值时,就确定了变量的数据类型,不用事先给变量指定数据类型3.2 静态语言:需要事先给变量进行数据类型定义所以综上所述,shell是一门解释型的弱类型动态语言
基本数据类型
数字类型
# int整型
定义:age=18
用于标识:年龄,等级,身份证号,qq号,个数# float浮点型
定义:salary=30000.0
用于标识:工资,身高,体重
字符串类型
#在shell中,加了引号的字符就是字符串类型定义:name='xiaohu'
用于标识:描述性的内容,如姓名,性别,国籍,种族# 注意1:字符串包含空格必须加引号
[root@master soft]# msg="hello xiaohu"
[root@master soft]# msg=hello xiaohu
bash: xiaohu: 未找到命令...# 注意2:连续的字符不加引号包含也是可以的,但我们还是强烈建议加上引号,规范一些
[root@master soft]# msg=hello
[root@master soft]# echo $msg
hello
[root@master soft]# # 注意3:单引号与双引号是不同的
" " 弱引用,引号的特殊字符有意义
' ' 强引用,引号内所有特殊字符都取消意义
[root@master soft]# name=“xiaohu”
[root@master soft]# echo "${name} is good"
xiaohu is good
[root@master soft]# echo '${name} is good'
${name} is good
弱类型语言
[root@master soft]# x=10
[root@master soft]# y="3"
[root@master soft]# expr $x + $y
13
数组类型
# 1、什么是数组?
数组就是一系列元素的集合,一个数组内可以存放多个元素# 2、为何要用数组?
我们可以用数组将多个元素汇总到一起,避免单独定义的麻烦
数组分为两种
• 普通数组:只能使用整数作为数组索引
• 关联数组【字典】:可以使用字符串作为数组索引,需要用declare -A声明
普通数组
=================声明普通数组=================
# 方式一:array=(元素1 元素2 元素3)
array=(xiaohu 18 male)# 方式二:array=([key1]=value1 [key2]=value2 [key3]=value3)
array=([1]=111 [0]="two" [2]=333)# 方式三:依次赋值
array_new[0]=111
array_new[1]=222
array_new[2]="third"# 方式四:利用执行命令的结果设置数组元素:array=($(命令)) 或者 array=(`命令`)
该方式会将命令的结果以空格为分隔符切成多个元素然后赋值给数组
[root@master soft]# ls /test
a.txt b.txt
[root@master soft]# array3=(`ls /test`)
[root@master soft]# declare -a |grep array3
declare -a array3='([0]="a.txt" [1]="b.txt")'# 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]# declare -A info
[root@master soft]# info["name"]="xiaohu"
[root@master soft]# info["age"]=18
[root@master soft]# info["gender"]="male"
[root@master soft]#
[root@master soft]# declare -A |grep info
declare -A info='([gender]="male" [name]="xiaohu" [age]="18" )'
[root@master soft]#
[root@master soft]# echo ${info[*]}
male xiaohu 18
[root@master soft]#
[root@master soft]# echo ${info["name"]}=================访问关联数组=================
declare -A info # 必须要先声明一下关联数组,才能进行赋值
[root@master soft]# info=(["name"]="xiaohu" ["age"]=18 ["gender"]="male")
[root@master soft]# echo ${info[0]}
xiaohu
[root@master soft]# echo ${info["age"]}
18
[root@master soft]# echo ${info["gender"]}
male
[root@master soft]#
2.5 变量值相关常见操作
获取变量值的长度
[root@master soft]# echo ${#url}
15# 已知变量msg='hello world!',请统计出变量中包含的字符数量
[root@master soft]# echo ${#msg}
12
切片
# ${paramter:offset:length}[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
截断【截掉】
# =================》一、砍掉左边的字符《=================
# 1.1 简单使用
[root@master soft]# url="www.shujia.com.cn"
[root@master soft]# echo ${url#www.}
shujia.com.cn# 1.2 结合 #* 非贪婪,默认情况下*是非贪婪,尽可能地少“吃”字符
[root@master soft]# echo ${url#*w}
ww.shujia.com.cn# 1.3 结合 ##* 贪婪,尽可能地多“吃”字符
[root@master soft]# echo ${url##*w} # *会尽可能多地吃掉字符,一直匹配到最远的那个w才停下来
.shujia.com.cn# =================》二、砍掉右边的字符《=================
# 1.1 简单使用
[root@master soft]# url="www.shujia.com.cn"
[root@master soft]# echo ${url%.cn}
www.shujia.com# 1.2 结合%.*非贪婪
[root@master soft]# echo ${url%.*}
www.shujia.com
# 1.3 结合%%.*贪婪
[root@master soft]# echo ${url%%.*}
www
let操作
# (1) 变量的值
[root@master soft]# j=1
[root@master soft]# let ++j
[root@master soft]# echo $j
2
[root@master soft]# ++ 表示的是自加1,
i++ 表示先赋值运算,再自加1
++i 表示先自加1,再做赋值运算-- 表示的自减1
i-- 表示先赋值运算,再自减1
--i 表示先自减1,再做赋值运算# (2) 表达式的值
[root@master soft]# unset i
[root@master soft]# unset j
[root@master soft]#
[root@master soft]# i=1
[root@master soft]# j=1
[root@master soft]#
[root@master soft]# let x=i++ # 先把i赋值给x,然后再++
[root@master soft]# let y=++j # 先++j,然后再把j的结果赋值给y
[root@master soft]# echo $i
2
[root@master soft]# echo $j
2
[root@master soft]# echo $x
1
[root@master soft]# echo $y
2
三、运算符
3.1 算术运算符
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | 'expr $a + $b' 为 30。 |
- | 减法 | 'expr $a-$b'结果为-10。 |
* | 乘法 | 'expr $a * $b' 结果为 200。 |
/ | 除法 | 'expr$b/$a'结果为2。 |
% | 取余 | 'expr $b % $a' 结果为0。 |
= | 赋值 | a=$b将把变量b的值赋给a |
== | 相等,用于比较两个数字,相同返回true | [$a == $b]返回false。 |
!= | 不相等,用于比较两个数字,不相同返回true | [$a != $b]返回true。 |
算术运算符需要配合以下操作符使用
# 浮点运算
bc # 整数运算
expr
$(())
$[]
let
bc 计算工具( yum install bc -y )
[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
expr
[root@master soft]# res=`expr 5 / 3` # 不支持浮点计算
[root@master soft]# echo $res
1[root@master soft]# res=`expr 1+1` # 注意:要有空格
[root@master soft]# echo $res
1+1
[root@master soft]# res=`expr 1 + 1`
[root@master soft]# echo $res
2如果是乘法,如需要转义\*
[root@master soft]# echo `expr 3 \* 10`
30
[root@master soft]#
$[]
[root@master soft]# echo $[$num1+$num2] # 等同于 echo $[num1+num2]
333
[root@master soft]# echo $[1.3+3.1]
-bash: 1.3+3.1: 语法错误: 无效的算术运算符 (错误符号是 ".3+3.1")
let赋值 只支持赋值操作
[root@master soft]# let res=1+1
[root@master soft]# echo $res
2
[root@master soft]#
[root@master soft]# let res=50/5
[root@master soft]# echo $res
10
[root@master soft]# let c=1.3*3
-bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3"
3.2 文件测试运算符
#!/bin/bash file="/var/node/test.sh"
if [ -r $file ]
thenecho "文件可读"
elseecho "文件不可读"
fiif [ -w $file ]
thenecho "文件可写"
elseecho "文件不可写"
fiif [ -x $file ]
thenecho "文件可执行"
elseecho "文件不可执行"
fiif [ -f $file ]
thenecho "文件为普通文件"
elseecho "文件为特殊文件"
fiif [ -d $file ]
thenecho "文件是个目录"
elseecho "文件不是个目录"
fiif [ -s $file ]
thenecho "文件不为空"
else
echo "文件为空"
fi
3.3 字符串测试运算符
# == 判断两个字符串是否相等
[root@master soft]# [ "aaa" == "aaa" ];echo $?
0# != 判断两个字符串是否不相等
[root@master soft]# [ "aaa" != "aaa" ];echo $?
1# -z 判断字符串长度是否为0
[root@master soft]# ip=""
[root@master soft]# [ -z "$ip" ];echo $? # 注意引号
0
[root@master soft]# ip='1.1.1.1'
[root@master soft]# [ -z "$ip" ];echo $?
1# -n 判断字符串长度是否不为0
[root@master soft]# [ -n "$ip" ];echo $? # 注意加引号
1
3.4 数值测试符(比较运算符)
运算符 | 说明 | 举例 |
---|---|---|
-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. |
[root@master soft]# [ 10 -eq 10 ];echo $?
0
[root@master soft]# [ 10 -eq 10 -a 10 \> 3 ];echo $?
0
3.5 关系运算符
需要结合(())进行使用
[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
3.6 赋值运算符
[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不是数字
3.7 布尔运算符
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为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。 |
[ $a -It 20 -a $b -gt 100 ];echo $?
3.8 逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的AND | [[$a -It 100 && $b-gt 100 ]]返回 false |
|| | 逻辑的OR | [[$a -It 100 || $b -gt 100 ]]返回 true |
四、流程控制
4.1 if语句
if 条件
then要执行的命令1要执行的命令2要执行的命令3...
fiif 条件;then要执行的命令1要执行的命令2要执行的命令3...
fi# 上述语法可以用一行代码代替
[ 条件信息 ] && xxx
双分支
if 条件;then要执行的命令1要执行的命令2要执行的命令3...
else要执行的命令1要执行的命令2要执行的命令3...
fi# 上述语法可以用一行代码代替
[ 条件信息 ] && xxx || xxxx
举例:
#!/bin/bash
username='xiaohu'
password='123'
read -p 'user: ' name
read -p 'passwd: ' passwdif [ $name == $username -a $passwd == $password ]
thenecho 'login successful'
elseecho 'username or password err'
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: ' nif [ $n -eq $age ];thenecho 'you get it'
elif [ $n -gt $age ];thenecho 'too big'
elif [ $n -lt $age ];thenecho 'too small'
fi======================版本2======================
#!/bin/bashread -p ">>> " num[[ ! $num =~ ^[0-9]+$ ]] && echo "请输入数字" && exitif [ $num -gt 18 ];thenecho "too big"
elif [ $num -lt 18 ];thenecho "too small"
elseecho "you got it"
fi
4.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 ];thenusername="default"
ficase $username in
root)echo "管理员用户";;
xiaohu)echo "普通用户";;
default)echo "默认用户";;
*)echo "其他用户"
esac
4.3 while循环
# 一、while语句结构:条件为真时,执行循环体代码
while 条件
do循环体
done# 二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do循环体
done
案例:
[root@master soft]# cat a.sh
#!/bin/bashx=0
while (($x<3))
doecho $x # 0,1,2let x++
doneecho "================"y=0
until (($y==3))
doecho $y # 0,1,2let y++
done[root@master soft]# ./a.sh
0
1
2
================
0
1
2
[root@master soft]#
流程控制语句:
continue:默认退出本次循环
break:默认退出本层循环
4.4 for循环
# Shell风格语法
for 变量名 [ in 取值列表 ]
do循环体
done# C语言风格语法
for ((初值;条件;步长))
do 循环体
done
案例1 shell风格
for i in {1..10}
doecho $i
done
案例2:C语言风格
for ((i=1;${i}<=10;i++))
doecho $i
done
4.5 扩展使用select
select var in ...
do...break
done
案例:
[root@master soft]# cat select2.sh
#!/bin/bash
PS3='choose one: ' # select默认使用PS3变量的值做提示符
echoselect var in $1 $2 $3 $4
doechoecho "your choose is $var"echo "OK"echobreak # 跳出select,否则是死循环
done[root@master soft]#
[root@master soft]# ./select2.sh 苹果 梨 蔬菜 香蕉 1) 苹果
2) 梨
3) 蔬菜
4) 香蕉
5) 茄子
choose one: 1your choose is 苹果
OK
五、函数
函数的定义
#语法:
[ function ] funname [()]
{命令1;命令2;命令3;...[return int;]
}# 示例1:完整写法
function 函数名() {函数要实现的功能代码
}# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {函数要实现的功能代码
}# 示例3:省略关键字function
函数名() {函数要实现的功能代码
}
函数的调用
# 语法:
函数名 # 无参调用
函数名 参数1 参数2 # 有参调用# 示例
function test1(){echo "执行第一个函数"
}function test2 {echo "执行第二个函数"
}test3(){echo "执行第三个函数"
}# 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
test1
test2
test3
函数调用时传递参数
# 调用函数test1,在其后以空格为分隔符依次罗列参数
test1 111 222 333 444 555
函数内部使用$n来取值
[root@master soft]# cat b.sh
function test1(){echo "...start..."echo $1echo $2echo $3echo "...end..."
}test1 111 222 333 444 555 # 为函数体传参[root@master soft]# ./b.sh
...start...
111
222
333
...end...
注意1:不要和脚本外部传参搞混淆
[root@master soft]# cat b.sh
function test1(){echo "...start..."echo "这是函数内:$1"echo "这是函数内:$2"echo "这是函数内:$3"echo "...end..."
}# test1 111 222 333 444 555
test1echo "这是脚本级的参数$1"
echo "这是脚本级的参数$2"
echo "这是脚本级的参数$3"[root@master soft]#
[root@master soft]#
[root@master soft]# ./b.sh xxx yyy zzz mmm nnn
...start...
这是函数内:
这是函数内:
这是函数内:
...end...
这是脚本级的参数xxx
这是脚本级的参数yyy
这是脚本级的参数zzz
[root@master soft]#
注意2:$*和$@
# 1、当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。# 2、"$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"# 3、"$@" 会把所有位置参数当成一个单独的字段,如果没有位置参数,则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1" "$2"等等
[root@master soft]# cat b.sh
echo "=======函数test1==========="
function test1(){echo "$*" # 111 222 333 444 555echo "$@" # 111 222 333 444 555echo $# # 5echo $$ # 87380echo $? # 0
}test1 111 222 333 444 555echo "=======函数test2==========="
function test2(){for i in "$*" # 注意:$*不加引号结果与$@一模一样doecho $idone
}test2 111 222 333 "444 555" # 注意"444 555"被引号引成了一个参数
# 运行结果为:111 222 333 444 555echo "=======函数test3==========="
function test3(){for i in "$@" # 注意:$*不加引号结果与$@一模一样doecho $idone
}test3 111 222 333 "444 555" # 注意"444 555"被引号引成了一个参数
# 运行结果为:
# 111
# 222
# 333
# 444 555
函数返回值的问题
1. 当函数体中没有return,那么最后一条命令(运行成功为0,其他不为0)作为返回值
[root@master soft]# cat b.sh
function test1(){echo 111echo 222echo 333xxx # 运行该命令出错
}test1
echo $?
[root@master soft]# ./b.sh
111
222
333
./b.sh:行5: xxx: 未找到命令
127
2. 有return的时候,注意,return后面只能跟整数并且【0-255】,在shell中0表示true, 1表示false
[root@master soft]# cat b.sh
function test1(){echo 111echo 222echo 333return 0
}test1
echo $? # 用$?获取函数的返回值
[root@master soft]# ./b.sh
111
222
333
0
局部变量
使用local关键字定义在函数内部的变量叫做局部变量,只能在函数内部使用
[root@master soft]# cat hello.sh
#!/bin/bash# 定义函数
function test(){local x=111echo "函数内访问x:$x"
}# 调用函数
testecho "在函数外即全局访问x:$x" # 无法访问函数内的局部变量
全局变量
可以在当前shell进程中使用, 所谓全局变量,就是降量在当前的整个Shell进程中都有效。每个Shell进程都有自己的作用域,彼此之间互不影响。在Shell中定义的变量,默认就都是全局变量。
[root@master soft]# cat hello.sh
#!/bin/bash
x=2222function test(){echo "函数内访问x:$x"
}
test
echo "在函数外即全局访问x:$x"
注意1:在函数内定义的变量,如果没有用local声明,那么默认也是全局变量
[root@master soft]# cat hello.sh
#!/bin/bash
function test(){x=2222 # 全局变量
}
test
echo "在函数外即全局访问x:$x" [root@master soft]# ./hello.sh
在函数外即全局访问x:2222
注意2:每执行一个解释器,都会开启一个解释的shell进程,每个shell进程都有自己的作用域彼此互不干扰
[root@master soft]# x=111 # 该变量仅仅只在当前shell进程中有效,对新的shell进程无影响
[root@master soft]# echo $x
111
[root@master soft]# bash # 执行bash解释器,则开启一个新的进程,或者干脆直接打开一个新的终端
[root@master soft]# echo $x[root@master soft]#
注意3:
全局变量的作用范围是当前的Shell进程,而不是当前的Shell脚本文件,它们是不同的概念。打开一个Shell窗口就创建了一个Shell进程,打开多个Shell窗口就创建了多个Shell进程,每个Shell进程都是独立的,拥有不同的进程ID。在—Shell进程中可以使用source命令执行多个Shell脚本文件,此时全局变量在这些脚本文件中都有效。
[root@master soft]# echo $x[root@master soft]# cat hello.sh
#!/bin/bash
function test(){x=2222 # 全局变量
}
test[root@master soft]# source hello.sh # 在当前shell进程中执行,产生一个全局变量x
[root@master soft]# echo $x # 在当前shell进程中访问全局变量x,可以看到
2222
[root@master soft]#
[root@master soft]#
[root@master soft]# cat aaa.sh
#!/bin/bash
echo $x[root@master soft]# source aaa.sh # 在当前shell进程中访问全局变量x,同样可以看到
2222
注意4:全局变量只在当前Shell进程中有效,对其它Shell进程和子进程都可以使用
如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。
环境变量被创建时所处的Shell进程称为父进程,如果在父进程中再创建一个新的进程来执行Shell命令,那么这
个新的进程被称作Shell子进程。当Shell子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变
量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。
[root@master soft]# export y=333 # 爷爷
[root@master soft]# bash # 爸爸
[root@master soft]# echo $y
333
[root@master soft]# bash # 孙子
[root@master soft]# echo $y
333ps:通过exit命令可以一层一层地退出 Shell。# 命令
set:显示所有变量
env:环境变量
注意5:环境变量只能向下传递不能向上传递
注意6:两个没有父子关系的shell进程不能传递环境变量
我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。
注意7:环境变量也是临时的
[root@master soft]# ps -ef
root 123436 0.0 0.1 116356 2960 pts/0 Ss 21:52 0:00 -bash
root 123492 0.0 0.1 116472 2932 pts/0 S 21:54 0:00 bash
root 123520 0.0 0.1 116440 2988 pts/0 S 21:54 0:00 bash注意:
# -开头的bash代表是在登录终端登录的顶级shell进程
# 非-开头的bash代表的是子shell进程
# 一旦退出了在终端登录的顶级shell,那么该终端下开启的所有子shell都会被回收,export设置的环境变量随即消失
练习题(晚上离开教室之前交)
# 1、脚本案例: 编写小脚本, 可以实现2位数加减乘除运算# 2、脚本案例: 编写判断输入参数是否是整数信息脚本# 3、如何利用脚本计算1+2+3+4..10总和数值# 4、根据考试成绩输出对应的礼物,90分以上爸爸给买电脑,80分以上爸爸给买手机, 60分以上爸爸请吃一顿大餐,60分以下爸爸给买学习资料。要求:该题使用多重if完成# 5、输入一批整数,使用循环求出最大值与最小值,输入0时结束。# 6、给20块钱买可乐,每瓶可乐3块钱,喝完之后退瓶子可以换回1块钱,问最多可以喝到多少瓶可乐。# 7、求九九乘法表