shell编程基础
shell概述
1. shell编程的意义
个人理解,shell编程类似于存储过程。存储过程是完成指定功能的一组sql语句集合,而shell编程就是完成指定功能的一组linux命令的集合。例如,我们要在服务器上部署一个一个项目,通常要做如下几个操作:
- 进入项目目录:cd /home/dubhlinn/project/demo
- 切分支:git checkout master
- 从远程仓库拉取最新代码:git pull
- 打包:mvn clean install -P production
- 部署:docker build -t ...
每次部署都要执行相同的一套流程,如果能把这些操作放在一个文件里,只需执行这个文件即可,会明显提高服务器运维管理的效率。这就是shell编程要满足的需求。
2. shell分类
linux的shell分类很多,例如
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
其中,最常用的是bash shell,即/bin/bash。
3. shell编程的格式
可以使用vi或vim编辑器创建shell脚本文件,其扩展名并没有要求,可以使用(.sh)表示它是一个shell文件,例如hello.sh、deploy.sh等,但是使用别的扩展名或者不用扩展名也不影响其执行。shell文件的第一行一般要指定shell种类,#!用来告诉系统,这个文件使用哪个解释器。例如最常用的bash shell这样写:
#!/bin/bash #这是单行注释 echo "hello world" :<<! 这是多行注释 这是多行注释 这是多行注释 ! echo "byebye"
4. shell脚本的执行方式
如果给shell脚本赋予了可执行权限,则直接写文件的路径名即可执行,可以使用绝对路径或相对路径,注意如果shell文件正好在当前目录下,相对路径要以 ./ 开头,而不能直接写文件名。如果并没有给shell脚本赋予可执行权限,则需要在文件路径名前加 sh。一般推荐第一种,即赋予用户对文件的可执行权限。例如,在当前路径(/home/dubhlinn)下有一个hello.sh的脚本,
(1) 当用户对其有可执行权限时:
./hello.sh
/home/dubhlinn/hello.sh
(2) 当用户对其无可执行权限时:
sh ./hello.sh
sh /home/dubhlinn/hello.sh
shell编程的变量
1. 变量的定义
变量名 = 变量值
#输出环境变量
echo $HOME
echo $USER
#自定义变量
A=12
B=hello
echo A=$A
echo B=$B
shell编程中变量分为两类:环境变量、自定义变量。环境变量是linux系统已经预先定义好的变量,可以直接使用;自定义变量必须先定义,再使用。定义变量的的语法很简单,但是有几点需要注意:
- 变量名可以由数字、字母、下划线组成,但是不能以数字开头
- 定义变量时,等号两侧不允许使用空格,这一点跟Java、JavScript等语言有区别
- 变量名习惯用大写字母表示
- 定义变量时不用美元符号($),使用变量时要使用美元符号($)
- 变量被定义后,可以重新赋值,后面的值会覆盖前面
2. 声明环境变量
(1) export 变量名=变量值
(2) source 文件路径名
第一步跟自定义变量相比,只是多了一个export关键字,第二步是为了让shell文件生效。完成这两步之后,可以在其它的shelle文件或中获取刚刚声明的环境变量。而相比之下,自定义变量仅限于在一个shell文件中使用,在其他的shell文件中无法使用。
#设置环境变量
export VILLA=Spain7
export TORRES=Spain9
需要注意的是,在一个shell文件中,自定义变量会覆盖同名的环境变量,例如
#输出环境变量
echo VILLA=$VILLA
#自定义变量
VILLA=Bacelona7
#输出,看是环境变量还是自定义变量
echo VILLA=$VILLA
第一次输出的是环境变量的值:Spain7,第二次输出的是自定义变量的值:Bacelona7。
3. 变量的撤销
unset 变量名
#撤销变量
B=hello
unset B
echo B=$B
4. 静态变量,不能被撤销
readonly 变量名=变量值
:<<!
静态变量
不能撤销
!
readonly C=5
echo C=$C
unset C
echo C=$C
执行这个脚本会报错,因为C是静态变量,不能使用unset将其撤销。
5. 将linux命令的执行结果赋给变量
变量名=`命令` 或 变量名=$(命令)
#将linux命令的执行结果赋给变量,两种方式等价
A=`ls -lh`
B=$(date)
echo A=$A
echo B=$B
6. 接收参数
在shell脚本中可以像Java方法一样接收形参,其语法格式如下
- $数字 表示第几个参数,超过10的需要用${数字},例如$1、$2、${15}
- $* 表示所有参数,且作为一个整体
- $@ 表示所有参数,但每个分别对待
- $# 表示参数的个数
#输出单个参数
echo 第1个参数为$1
echo 第2个参数为$2
echo 第10个参数为${10}
#输出全部参数,且作为一个整体
echo 全部参数为$*
#输出全部参数,且分别对待
echo 参数分别为$@
#输出参数个数
echo 总共有$#个参数
在执行这个shell脚本时,可以传入参数,例如 ./hello.sh 1 2 3
shell编程的运算符
shell的运算符本身跟Java、JavaScript等语言基本一样:+、-、*、/、%等,但实际应用有一些独特的规则,主要有以下三种使用方法
- $((运算式))
- $[运算式]
- expr 运算式
其中,前两种方式的运算符之间不能加空格,第三种方式的运算符之间必须加空格,而且括号、乘号需要加转义符号,即 \(、\)、\* 。这些规则看起来有点奇怪,下面用几个具体的示例来说明。
1. 求 (3+5)*4/(2+1)的值
#使用第一种方式计算
R1=$(((3+5)*4/(9-1)))
echo R1=$R1
#使用第二种方式计算
R2=$[(3+5)*4/(9-1)]
echo R2=$R2
#使用第三种方式计算
R3=`expr \( 3 + 5 \) \* 4 / \( 9 - 1 \)`
echo R3=$R3
2. 求第一个参数和第二个参数的和,然后取除以第三个参数的余数
#使用第一种方法计算
R1=$((($1+$2)%$3))
echo R1=$R1
#使用第二种方法计算
R2=$[($1+$2)%$3]
echo R2=$R2
#使用第三种方法计算
R3=`expr \( $1 + $2 \) % $3`
echo R3=$R3
shell编程的条件分支语句
1. if条件分支语句
if [ 条件1 ]
then
语句1
elif [ 条件2 ]
then
语句2
else
语句3
fi
其中,中括号与条件之间必须加空格。
符号 | 意义 |
= | 两个字符串是否相等 |
-lt | 小于 |
-le | 小于等于 |
-eq | 等于 |
-ge | 大于等于 |
-gt | 大于 |
-ne | 不等于 |
-r | 文件或目录有读的权限 |
-w | 文件或目录有写的权限 |
-x | 文件或目录有执行权限 |
-f | 文件存在并且是一个常规文件 |
-e | 文件存在 |
-d | 文件存在并且是一个目录 |
2. case条件分支语句
case 变量 in
值1)
语句1
;;
值2)
语句2
;;
*)
语句3
;;
esac
3. 应用示例
#判断文件是否存在
if [ -e /home/dubhlinn/shell/aaa.txt ]
then
echo 存在
else
echo 不存在
fi
#判断是不是一个目录
if [ -d /home/dubhlinn/shell ]
then
echo 是一个目录
else
echo 不是一个目录
fi
#判断文件是否有可执行权限
if [ -x /home/dubhlinn/shell/hello.sh ]
then
echo 有可执行权限
else
echo 无可执行权限
fi
#判断两个数字的大小
if [ $[5*3] -ge $[6+7] ]
then
echo 前者大于等于后者
else
echo 前者小于后者
fi
#判断传入的参数范围
if [ $1 -gt 90 ]
then
echo 优秀
elif [ $1 -ge 60 ]
then
echo 及格
else
echo 不及格
fi
#!/bin/bash
:<<!
如果参数是6,则输出"星期六",
如果参数是7,则输出"星期日"
如果是其他,则输出"工作日"
用两种判断语句实现
!
#使用if语句实现
if [ $1 -eq 6 ]
then
echo 星期六
elif [ $1 -eq 7 ]
then
echo 星期日
else
echo 工作日
fi
#使用case语句实现
case $1 in
6)
echo 星期六
;;
7)
echo 星期日
;;
*)
echo 工作日
;;
esac
shell编程的循环语句
1. for循环
for ((初始值;循环条件;变量变化))
do
表达式
done
2. while循环
while [ 条件 ]
do
表达式
变量变化
done
注意,中括号跟条件之间需要加空格。
3. 使用示例
#!/bin/bash
:<<!
分别使用for循环和while循环
计算从1到控制台参数的累加之和
例如,控制台输入100,则计算从1-100之和
!
#使用for循环
SUM1=0
for ((i=0;i<=$1;i++))
do
SUM1=$[$SUM1+$i]
done
echo SUM1=$SUM1
#使用while循环
SUM2=0
j=1
while [ $j -le $2 ]
do
SUM2=$[$SUM2+j]
j=$[$j+1]
done
echo SUM2=$SUM2
控制台交互
1. read关键字
read [-p 提示信息] [-t 等待时间] 参数
其中,-p后面接提示信息,显示在控制台;-t后面接等待时间(单位是秒),超过这个时间没有响应则自动退出;参数是用来接收用户在控制台输入的值的变量名。
2. 使用示例
#/bin/bash
#提示用户输入一个数字,然后打印到控制台
read -p "请输入一个数字" A
echo 您输入的数字是: $A
#提示用户输入一个数字,然后打印到控制台,最多等待5秒
read -p "请在5秒钟之内输入一个不等于零的数" -t 5 B
if [ $B -ne 0 ]
then echo 您输入的数字是:$B
else echo 输入无效
fi
shell函数
shell编程跟其他编程语言一样,也有函数(Java中叫方法)。shell函数分为系统函数和自定义函数,跟变量一样,系统函数是linux系统已经定义好的,我们可以直接拿来用;自定义函数需要自己先定义,再调用。
1. basename函数
basename 全路径 [后缀]
basename是一个系统函数,用于返回文件名,即最后一个/后面的部分,如果加了中括号内的后缀,表示连后缀也去掉。例如
#!/bin/bash
#返回一个全路径的文件名
basename /home/dubhlinn/shell/aaa.sh
#返回一个全路径的文件名,去掉后缀
basename /home/dubhlinn/shell/aaa.sh .sh
2. dirname函数
dirname 全路径
dirname也是一个系统函数,它与basename正好相反,返回的是目录名,即最后一个/之前的部分。
#!/bin/bash
#返回一个全路径的目录名
dirname /home/dubhlinn/shell/aaa.sh
3. 自定义函数的定义格式
function 函数名() {
语句;
[return 返回值;]
}
4. 自定义函数的调用格式
函数名 参数
5. 自定义函数使用示例
#!/bin/bash
:<<!
定义一个函数,求两个参数之和
!
#定义函数
function getSum() {
SUM=$[$A+$B]
echo 函数调用结构为$SUM
}
#调用函数
A=5
B=6
getSum $A $B