概述
什么是shell脚本:
Shell脚本是利用shell的功能所写的一个程序。这个程序是使用纯文本文件(后缀为.sh),将一些shell的语法与命令(含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能。Shell脚本实际上就是Shell命令的集合。
编写运行shell脚本:
- 编写:使用vi,创建一个文本,以.sh为结尾
- 代码:首先声明shell类型:#!/bin/bash。之后用shell命令进行编写
- 运行:bash <脚本名>、source <脚本名>、直接脚本名(这需要脚本文件有可执行权限)
示例如下:
Shell变量
1、基本知识
shell中变量的命名要求与C类似:
- 区分大小写
- 不能使用程序内的保留字和内置变量(类似C不能使用关键字)
- 只能使用数字、字母、下划线,且不能以数字开头(一般使用大写字母)
变量的分类:
变量分为:自定义变量和特殊变量。特殊变量如下
- 环境变量:由系统维护,用于设置工作环境
- 只读变量:用于变量值不允许被修改的情况
- 位置变量:通过命令行给脚本程序传递参数
- 预定义变量:bash中内置的一类变量,不能直接修改
2、变量相关操作
2.1 变量的基本操作
- 定义赋值变量:<变量名>=<变量值>(等号两边不能有空格)
- 取出变量值:$<变量名>
- 取消变量定义:unset<变量名>
- 注释:#
- 双引号:允许引用其他变量值
- 单引号:禁止引用其他变量值,引号中的内容为原样输出
- 反撇号:命令替换,把命令的输出结果进行输出,与$()作用一致。
2.2 变量数组的操作
- 数组成员初始化:<数组名>=(<数组成员值>)(等号两边没有空格,数组成员值用空格分隔)
- 数组成员赋值:<数组名>[下标]=<变量值>。如:a[0]=10(等号两边没有空格)
- 访问数组成员:${数组名[下标]}。假设数组名为a,以下为访问示例
- 显示第一个元素:${a[0]} 或者 $a
- 显示第n个元素:${a[n]}
- 显示最后一个元素:${a[-1]}
- 显示数组全部元素:${a[*]} 或者 ${a[@]}
- 显示数组元素个数:${#a[*]} 或者 ${#a[@]}
- 删除数组:可以使用unset删除数组成员、删除整个数组
- 删除数组成员:unset <数组名>[下标]。删除之后,值为空。例如删除a[0],那么a[0]=空
- 删除整个数组:unset <数组名>
- 显示以指定元素开始的指定个数的元素:${a[@]:起始元素:元素个数}
2.3 只读变量
只读变量常常存放传入的文件名参数,代表不能修改文件名。
只读变量不能使用unset删除,只读变量只在当前终端有效,因此可以关闭终端来强制删除
- 定义只读变量:readonly <变量名>=<变量值>
2.4 位置变量与预定义变量
- 传参:bash <shell脚本名> <参数>(参数用空格隔开)
- 第一个参数:$0,值为shell脚本名
- 第n个参数:$n,$(n)。n为多位数时用()。如:$1,$(10)
- 总参数个数:$#。$0不进行总参数个数的统计
- 全部传入的参数值:$@(全部参数当作一个整体)或者$*(每个参数分开是一个值)
- 上一条指令执行的结果:$?(常用作test指令返回值的判断,$?值:test真返回0)
- 当前进程的id号:$$
补充:$@与$*的区别验证
2.4 环境变量
- $USER:表示用户名称
- $HOME:表示用户的宿主目录
- $LANG:表示语言和字符集
- $PWD:表示当前所在工作目录
- $PATH:表示可执行用户程序的默认路径
- export:可以将变量指定为全局变量。默认变量只能在当前终端使用,其他的shell不能使用
2.5 局部变量与全局变量
在shell中定义的变量默认为全局变量,在整个shell中都可以去访问。
使用local可以使变量变为局部变量。
注意:当函数为正常调用时,全局变量可以被函数外部访问。当函数是命令置换``调用时,全局变量不能被函数外部访问。
Shell语句
shell语句分为说明型语句、功能型语句、结构型语句。说明型语句就是注释;功能型语句就是shell命令,如:grep、echo等。结构型语句就是像循环、判断等语句。
1、功能型语句补充
1.1 获取键盘输入数据read
read可以获取键盘上输入的数据,并存入指定的变量中。
命令:
read <选项> 变量(多个变量用空格隔开)
选项:
- -s:键盘输入时不显示输入的内容
- -t <时间>:设置输入的时间,单位s。超过时间会自动退出输入
- -p "提示":设置提示语句
1.2 算术运算命令expr
算术运算命令可以实现加(+)、减(-)、乘(\*)、除(/)、求模(%)运算
命令:expr <运算值1> <运算符> <运算值2>(运算值与运算符直接必须要有空格)
注意:expr是一个命令,所以赋值给变量运算值时需要加上``来取命令的值进行赋值
1.3 测试语句test
test可对字符串、整型、文件属性进行判断。
判断结果可以用$?来查看,当结果为真时,$?返回0;当结果为假时,$?返回1;
字符串测试
注意:在判断时,字符串需要加上双引号
- "s1" = "s2":测试两个字符串的内容是否完全一样
- "s1" != "s2":测试两个字符串的内容是否有差异
- -z "s1":测试s1字符串的长度是否为0,
- -n "s1":测试s1字符串的长度是否不为0
整型测试
- a -eq b:测试a与b是否相等(eq:equal)
- a -ne b:测试a与b是否不相等(ne:not equal)
- a -gt b:测试a是否大于b(greater than)
- a -ge b:测试a是否大于等于b(greater equal)
- a -lt b:测试a是否小于b(less than)
- a -le b:测试a是否小于等于b(less equal)
文件测试
文件类型与ls -l列出的文件类型符号是对应的(bcd-lsp),除了普通文件的 " - " 换成了 " f "。
具体含义在2.Linux_基础-CSDN博客的 "文件-文件类型" 章节。
- -e name:测试一个文件是否存在(exist)
- -d name:测试name是否为一个目录(bcd-lsp)
- -f name:测试name是否为普通文件(bcd-lsp) 如果软连接指向的为普通文件,则该软连接也可以通过该test判断
- -L name:测试name是否为符号链接(bcd-lsp)
- -r name:测试name文件是否存在且为可读(rwx)
- -w name:测试name文件是否存在且为可写(rwx)
- -x name:测试name文件是否存在且为可执行(rwx)
- -s name:测试name文件是否存在且其长度不为0
- f1 -nt f2:测试文件f1是否比文件f2更新(new than)
- f1 -ot f2:测试文件f1是否比文件f2更旧(old than)
2、结构型语句
结构型语句包括:条件测试语句(两路分支)、多路分支语句、循环语句、循环控制语句等
2.1 分支语句
2.1.1 类似 if-else
- 表达式
if后跟判断表达式。判断表达式可以是test命令,也可以是用中括号来代替test。以判断文件是否存在为例,假设变量名为file。
方式1:if test -e $file 方式2:if [ -e $file ] (使用[ ]时,左右必须有空格)
- 逻辑连接符
与:-a。例如:if [ -e "$file" -a -f "$file" ] 判断file值存在并且file是一个普通文件
或(第一种):-o。例如:if [ -d "$file" -o -f "$file" ] 判断file是目录或者file是一个普通文件
或(第二种):||。例如:if [ -d "$file" ] || [ -f "$file" ] 使用||时,需要有两个[ ]
非:!。例如:if [ ! -d "$file" ] 判断file不是目录
if-else语法
注意:if 和 fi 要成对出现
if <判断表达式>
then
条件为真时的表达式
else
条件为假时的表达式
fi
示例:判断输入用户名是否存在
用户名存放在/etc/passwd文件中的第一列。
if-elseif-else语法
注意:if 和 elif 后都要跟一个then
if <判断表达式>
then
if条件为真时的表达式
elif <判断表达式>
then
elif条件为真时的表达式
else
条件为假时的表达式
fi
示例:判断输入的文件类型
2.1.2 类似 switch-case
当模式为多个时,可以用" | "来进行连接。
当模式值为*时,类似switch-case中的default
语法:
case 字符串变量 in
模式1) #多种情况时,可以用 | 来连接
代码
;; #这类似switch-case里的break ;;必须加,否则会报错
模式2)
代码
;; #最后一个模式可以省略;;
esac
示例:将学生成绩进行分类,80~100的为A,0~79的为B。其余成绩是错误数据
2.2 循环语句
2.2.1 类似 for
for从"表"中拿数据赋值给"变量",拿一个数据循环一次,循环次数就是"表"中的数据个数。
for后跟的可以是 "变量 in 表" 这种形式,也可以是(( ))类似C语言语法的形式。
语法:
for 变量 in 表
do
代码xxx
done
示例1:获取输入的参数
示例2:for的用法
2.2.2 类似 while
while后跟的表达式,可以是if后那种[ ]形式,也可以为(( ))这种C语言语法形式。
语法:
while 表达式
do
代码xxx
done
示例:for的用法
2.2.3 break、continue
break与continue的使用与C中的完全一致。区别点是代码后面不需要加分号。
2.3 退出exit
当shell脚本执行到exit时,代表该脚本退出,后面的脚本代码不再执行
2.4 函数
语法:
function 函数名(){
代码段
return xxx #没有return值可以不写
}
获取return值:
在调用函数之后,使用$?来获取return的数值。注意:函数只能return整型数字
获取函数中打印到标准输出(终端)的值:
使用 `函数名` 来获取函数中打印到标准输出的值。
函数传参:
函数传参就是直接在函数名后面加入参数即可,函数获取参数是通过位置变量来获取。
局部变量与全局变量:
在shell中定义的变量默认为全局变量,在整个shell中都可以去访问。
使用local可以使变量变为局部变量。
注意:当函数为正常调用时,全局变量可以被函数外部访问。当函数是命令置换``调用时,全局变量不能被函数外部访问。