前言
在Linux学习过程中,我们无可避免的会碰到一个既让人喜欢,又令人头疼的神奇的东西——bash编程,即shell脚本。那么什么是shell脚本呢?shell是一个命令语言解释器,而shell脚本则是Linux命令的集合,按照预设的顺序依次解释执行,来完成特定的、较复杂的系统管理任务,类似于Windows中的批处理文件。本文带来的是bash编程的基础语法讲解。
bash编程之变量
bash变量类别
本地变量:只对当前shell进程有效的变量,对其它shell进程无效,包当前shell进程的子进程
1 |
|
变量赋值:向变量的存储空间保存数据
变量引用:${VAR_NAME}
1 2 |
|
环境变量:对当前shell进程及其子shell有效,对其它的shell进程无效
1 2 3 4 |
|
局部变量:对shell脚本中某代码片断有效,通常用于函数本地
1 |
|
位置变量:用来接受变量指定位置的参数
1 |
|
特殊变量:shell对一些参数做特殊处理,这些参数只能被引用而不能被赋值
1 2 3 4 5 6 7 |
|
查看变量:
1 2 |
|
变量命名:
1、不能使用程序中的关键字(保留字)
2、只能使用数字、字母和下划线,且不能以数字开头
3、要见名知义
变量类型:
数值型:精确数值(整数),近似数值(浮点型)
字符型:char,string
布尔型:true, false
类型转换:显式转换,隐式转换
bash的配置文件:
profile类:为交互式登录的用户提供配置
全局:/etc/profile、/etc/profile.d/*.sh
用户:~/.bash_profile
bashrc类:为非交互式的用户提供配置
全局:/etc/bashrc
用户:~/.bashrc
功能:设定本地变量,定义命令别名
bash编程之编写格式及执行方式
编写格式:
shell脚本第一行必须顶格写,用shebang定义指定的解释器来解释该脚本。
1 |
|
其它的以#开头的行均为注释,会被解释器忽略,可用来注释脚本用途及版本,方便使用管理。
执行方式:
bash编程属于面向过程编程,执行方式如下:
顺序执行:按命令先后顺寻依次执行
选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支
循环执行:将同一段代码反复执行多次,因此,循环必须有退出条件;否则,则陷入死循环
bash执行选项:
1 2 |
|
bash之算数运算与逻辑运算
算数运算
定义整型变量:
1 2 |
|
实现算术运算的方式:
1 2 3 4 |
|
算术运算符:
1 2 3 4 5 6 |
|
注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算,bash会执行变量类型的隐式类型转换。
逻辑运算
1 2 3 4 5 6 7 8 9 10 11 |
|
bash编程之条件测试语句
bash条件测试
整型测试:整数比较
例如 [ $num1 -gt $num2 ]
1 2 3 4 5 6 |
|
字符测试:字符串比较
双目:
例如[[ "$str1" > "$str2" ]]
1 2 3 4 5 6 |
|
单目:
1 2 |
|
文件测试:判断文件的存在性及属性等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
组合条件测试:在多个条件间实现逻辑运算
1 2 3 4 5 6 7 8 9 10 11 |
|
条件测试之if语句
if语句之单分支
语句结构:
1 2 3 |
|
表示条件测试状态返回值为值,则执行选择分支
例:写一个脚本,接受一个参数,这个参数是用户名;如果此用户不存在,则创建该用户;
1 2 3 4 |
|
if语句之双分支
语句结构:
1 2 3 4 5 |
|
两个分支仅执行其中之一
例:通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;
1 2 3 4 5 6 |
|
注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。
补充:bash交互式编程
read [option] “prompt”
1 2 3 |
|
例:输入用户名,可返回其shell
1 2 3 4 5 6 7 |
|
if语句之多分支
语句结构:
1 2 3 4 5 6 7 8 9 10 |
|
例:传递一个用户名给脚本:如果此用户的id号为0,则显示说这是管理员;如果此用户的id号大于等于500,则显示说这是普通用户;否则,则说这是系统用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
条件测试之case语句
case语句:有多个测试条件时,case语句会使得语法结构更明晰
语句结构:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
PATTERN:类同于文件名通配机制,但支持使用|表示或者
1 2 3 4 |
|
例:写一个脚本,完成如下任务,其使用形式如下所示:
script.sh {start|stop|restart|status}
其中:
如果参数为空,则显示帮助信息,并退出脚本;
如果参数为start,则创建空文件/var/lock/subsys/script,并显示“starting script successfully.”
如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop script successfully.”
如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restarting script successfully.”
如果参数为status,那么:如果文件/var/lock/subsys/script存在,则显示“Script is running…”,否则,则显示“Script is stopped.”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
bash编程之循环语句
循环之for循环
for语句格式一
语句结构:
1 2 3 |
|
列表:可包含一个或多个元素
循环体:依赖于调用变量来实现其变化
循环可嵌套
退出条件:遍历元素列表结束
例:求100以内所有正整数之和
1 2 3 4 5 6 |
|
for语句格式二
1 2 3 |
|
先用初始条件和测试条件做判断,如果符合测试条件则执行循环体,再修改表达式,否则直接跳出循环。
例:求100以内所有正整数之和(for二实现)
1 2 3 4 5 6 |
|
循环之while语句
while适用于循环次数未知,或不便用for直接生成较大的列表时
语句结构:
1 2 3 |
|
测试条件为真,进入循环;测试条件为假,退出循环
例:求100以内所有偶数之和,要求使用取模方法
1 2 3 4 5 6 7 8 9 10 |
|
例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止
1 2 3 4 5 6 7 8 9 10 |
|
while特殊用法:遍历文本文件
语句结构:
1 2 3 |
|
变量名,每循环一次,记忆了文件中一行文本
例:显示ID号为偶数,且ID号同GID的用户的用户名、ID和SHELL
1 2 3 4 5 6 7 |
|
循环之until语句
语句结构:
1 2 3 |
|
测试条件为假,进入循环;测试条件为真,退出循环
例:求100以内所有偶数之和,要求使用取模方法(until实现)
1 2 3 4 5 6 7 8 9 10 |
|
例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止(until实现)
1 2 3 4 5 6 7 8 9 10 |
|
循环之循环控制和shift
循环控制命令:
break:提前退出循环
break [N]: 退出N层循环;N省略时表示退出break语句所在的循环
continue: 提前结束本轮循环,而直接进入下轮循环
continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环
死循环:
1 2 3 4 5 6 7 8 |
|
例:写一个脚本,判断给定的用户是否登录了当前系统
(1) 如果登录了,则脚本终止;
(2) 每5秒种,查看一次用户是否登录;
1 2 3 4 5 6 7 8 9 |
|
shift:如果没有数字,只有shift 就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2 ,跳过两个参数获取下一个参数。
例:写一个脚本,使用形式如下所示
showifinfo.sh [-i INTERFACE|-a] [-v]
要求:
1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;
显示接口的ip地址
2、使用-v,则表示显示详细信息
显示接口的ip地址、子网掩码、广播地址;
3、默认表示仅使用-a选项;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
bash编程之函数
语法结构:
1 2 3 4 5 6 7 |
|
可调用:使用函数名,函数名出现的地方,会被自动替换为函数
函数的返回值:
函数的执行结果返回值:代码的输出
函数中使用打印语句:echo, printf
函数中调用的系统命令执行后返回的结果
执行状态返回值:
默认取决于函数体执行的最后一个命令状态结果
自定义退出状态码:return [0-255]
注意:函数体运行时,一旦遇到return语句,函数即返回;
函数可以接受参数:
在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数
$1, $2, …
$#, $*, $@
例:写一个脚本,完成如下功能(使用函数):
1、提示用户输入一个可执行命令;
2、获取这个命令所依赖的所有库文件(使用ldd命令);
3、复制命令至/mnt/sysroot/对应的目录中
解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;
4、复制各库文件至/mnt/sysroot/对应的目录中;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
bash编程之信号捕捉
trap命令用于在shell程序中捕捉到信号,之后可以有三种反应方式:
(1)执行一段程序来处理这一信号
(2)接受信号的默认操作
(3)忽视这一信号
trap对上面三种方式提供了三种基本形式:
第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双引号中的命令串。
trap 'commands' signal-list
trap "commands" signal-list
第二种形式的trap命令恢复信号的默认操作:trap signal-list
第三种形式的trap命令允许忽视信号:trap " " signal-list
trap 'COMMAND' SIGINT(表示关闭进程)
例:写一个脚本,能够ping探测指定网络内的所有主机是否在线,当没有执行完时可接收ctrl+c命令退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
bash编程之数组
数组:连续的多个独立内存空间,每个内存空间相当于一个变量
数组元素:数组名+索引(从0开始编号)
索引的表示方式:a[0], a[1]
声明数组:declare -a ARRAR_NAME
关联数组:declare -A ARRAY_NAME
支持稀疏格式:仅一维数组
数组元素的赋值:
(1) 一次只赋值一个元素
a[0]=$RANDOM
…
(2) 一次赋值全部元素
a=(red blue yellow green)
(3) 指定索引进行赋值
a=([0]=green [3]=red [2]=blue [6]=yellow)
(4) 用户输入
read -a ARRAY
数组的访问:
用索引访问:ARRAY[index]
数组的长度:
${#ARRAY[*]}
${#ARRAY[@]}
例:写一个脚本,生成10个随机数,保存至数组中;而后显示数组下标为偶数的元素
1 2 3 4 5 |
|
从数组中挑选某元素:
${ARRAY[@]:offset:number}
切片:
offset: 偏移的元素个数
number: 取出的元素的个数
${ARRAY[@]:offset}:取出偏移量后的所有元素
${ARRAY[@]}: 取出所有元素
数组复制:
要使用${ARRAY[@]}
$@: 每个参数是一个独立的串
$*: 所有参数是一个串
向数组中追加元素:非稀疏格式
week,
week[${#week[@]}]
从数组中删除元素:
unset ARRAY[index]
例:复制一个数组中下标为偶数的元素至一个新数组中
1 2 3 4 5 6 7 8 9 10 11 |
|
例:生成10个随机数,升序排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
例:打印九九乘法表
1 2 3 4 5 6 7 8 9 10 |
|
bash编程之字符串操作
字符串切片:
${string:offset:length}
1 2 3 |
|
取尾部的指定个数的字符:
${string: -length}
1 2 |
|
取子串:基于模式
${variable#*word}:在variable中存储字串上,自左而右,查找第一次出现word,删除字符开始至此word处的所有内容;
${variable##*word}:在variable中存储字串上,自左而右,查找最后一次出现word,删除字符开始至此word处的所有内容;
1 2 3 |
|
${variable%word*}: 在variable中存储字串上,自右而左,查找第一次出现word,删除此word处至字串尾部的所有内容;
${variable%%world*}:在variable中存储字串上,自右而左,查找最后一次出现word,删除此word处至字串尾部的所有内容;
1 2 3 |
|
例:url="http://www.redhat.com:80"
1 2 |
|
查找替换:
${variable/pattern/substi}: 替换第一次出现
1 2 3 4 5 |
|
${variable//pattern/substi}:替换所有的出现
1 2 |
|
${variable/#pattern/substi}:替换行首被pattern匹配到的内容
1 2 |
|
${variable/%pattern/substi}:替换行尾被pattern匹配到的内容
1 2 |
|
pattern可以使用globbing中的元字符:* ?
查找删除:
${variable/pattern}:删除第一次出现
1 2 |
|
${variable//pattern}:删除所有的出现
1 2 |
|
${variable/#pattern}:删除行首被pattern匹配到的内容
1 2 |
|
${variable/%pattern}:删除行尾被pattern匹配到的内容
1 2 |
|
大小写转换:
小–>大:${variable^^}
1 2 |
|
大–>小:${variable,,}
1 2 3 |
|
变量赋值操作:
${variable:-string}:variable为空或未设定,那么返回string,否则,返回variable变量的值;
${variable:=string}:variable为空或未设定,则返回string,且将string赋值给变量variable,否则,返回variable的值;
为脚本使用配置文件,并确保某变量有可用值的方式
variable=${variable:-default vaule}
写个脚本,配置etc目录;
(1) 在配置文件中定义变量;
(2) 在脚本中source配置文件;
1 2 3 4 5 |
|
bash编程之补充
mktemp命令:
mktemp [OPTIONS] filename.XXX
1 2 |
|
1 2 3 |
|
install命令:
install [OPTIONS] SOURCE DEST
install [OPTIONS] SOURCE… DIR
install [OPTIONS] -d DIR …
增强型的复制命令:
1 2 3 |
|
1 |
|
1 2 3 4 5 |
|
The end
至此,bash编程的所有基础语法知识点就总结完毕了,掌握了以上知识点,bash编程算是略有小成了吧,如果有兴趣可继续钻研高级语法,目前能力不足就不介绍啦。这里给大家推荐两本bash编程的书籍,能力相对较弱的可以看一下《Linux命令行和shell编程宝典》,有能力的就看《abs-guide》吧。看在码字辛苦的份上,多多点赞吧。