Shell脚本编程
Shell脚本编程

在ROS中我们很难避免使用Linux,为了简化一些ROS或者linux的操作,我们还要学会使用和编写Shell脚本,本教程将深入带你编写Shell脚本。如果您对 Linux和Shell 熟练掌握,请自行跳过该篇。
第一章 编程基础
Linus:Talk is cheap, show me the code
1 程序组成
程序:算法+数据结构数据:是程序的核心算法:处理数据的方式
数据结构:数据在计算机中的类型和组织方式
2 程序编程风格

过程式:以指令为中心,数据服务于指令,shell脚本程序提供了编程能力,解释执行对象式:以数据为中心,指令服务于数据,java,C#,python等
3 编程语言
计算机:运行二进制指令
编程语言:人与计算机之间交互的语言。分为两种:低级语言和高级语言低级编程语言:
机器:二进制的0和1的序列,称为机器指令。与自然语言差异太大,难懂、难写
汇编:用一些助记符号替代机器指令,称为汇编语言
如:ADD A,B 将寄存器A的数与寄存器B的数相加得到的数放到寄存器A中汇编语言写好的程序需要汇编程序转换成机器指令
汇编语言稍微好理解,即机器指令对应的助记符,助记符更接近自然语言高级编程语言:

编译:高级语言-->编译器-->机器代码文件-->执行,如:C,C++
解释:高级语言-->执行-->解释器-->机器代码,如:shell,python,php,JavaScript,perl
编译和解释型语言
4 编程逻辑处理方式
三种处理逻辑:顺序执行选择执行循环执行

第二章 shell脚本语言的基本结构
1 shell脚本的用途
自动化常用命令
执行系统管理和故障排除
创建简单的应用程序
处理文本或文件
2 shell脚本基本结构
shell脚本编程:是基于过程式、解释执行的语言编程语言的基本结构:
各种系统命令的组合
数据存储:变量、数组
表达式:a + b
控制语句:if
shell脚本:包含一些命令或声明,并符合一定格式的文本文件格式要求:首行shebang机制
3 创建shell脚本过程
第一步:使用文本编辑器来创建文本文件第一行必须包括shell声明序列:#!
示例:
添加注释,注释以#开头第二步:加执行权限
给予执行权限,在命令行上指定脚本的绝对或相对路径
第三步:运行脚本
直接运行解释器,将脚本作为解释器程序的参数运行
4 脚本注释规范
第一行一般为调用使用的语言
程序名,避免更改文件名为无法找到正确的文件
版本号
更改后的时间
作者相关信息
该程序的作用,及注意事项
最后是各版本的更新简要说明
5 第一个脚本
shell脚本范例:
6 脚本调试
检测脚本中的语法错误
调试执行
7 变量
7.1 变量
变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据
7.2 变量类型
变量类型:
内置变量,如:PS1,PATH,HISTSIZE
用户自定义变量
不同的变量存放的数据不同,决定了以下
数据存储方式
参与的运算
表示的数据范围
变量数据类型:
字符
数值:整型、浮点型,bash 不支持浮点数
7.3 编程语言分类

静态和动态语言
静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c
动态编译语言:不用事先声明,可随时改变类型,如:bash,Python
强类型和弱类型语言
强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如java , c# ,python
如:以下python代码
弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会 自动进行隐式类型转换;变量无须事先定义可直接调用
如 :bash ,php,javascript
7.4 Shell中变量命名法则
不能使程序中的保留字:如:if, for
只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”
见名知义,用英文名字,并体现出实际作用
统一命名规则:驼峰命名法, studentname,大驼峰StudentName 小驼峰studentName 变量名大写
局部变量小写函数名小写
7.5 变量定义和引用
变量的生效范围等标准划分变量类型
普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell 进程均无效
环境变量:生效范围为当前shell进程及其子进程
本地变量:生效范围为当前shell进程中某代码片断,通常指函数
变量赋值:
value 可以是以下多种形式
弱引用和强引用
"$name " 弱引用,其中的变量引用会被替换为变量值
'$name '强引用,其中的变量引用不会被替换为变量值,而保持原字符串
显示已定义的所有变量:
删除变量:
范例:
7.6 环境变量
变量声明和赋值:
变量引用:
显示所有环境变量:
删除变量:
bash内建的环境变量:
7.7 只读变量
只读变量:只能声明定义,但后续不能修改和删除
声明只读变量:
查看只读变量:
7.8 位置变量
位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数
清空所有位置变量
7.9 退出状态码变量
进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255
范例:
用户可以在脚本中使用以下命令自定义退出状态码
注意:
脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
7.10 展开命令行
展开命令执行顺序
防止扩展
加引号来防止扩展
变量扩展
7.11 脚本安全和set
set 命令:可以用来定制shell环境
$-变量
h:hashall,打开选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭
i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,
在脚本中,i选项是关闭的
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
set命令实现脚本安全
-u 在扩展一个没有设置的变量时,显示错误信息, 等同set -o nounset
-e 如果一个命令返回一个非0退出状态值(失败)就退出, 等同set -o errexit
-o option 显示,打开或者关闭选项显示选项:set -o
打开选项:set -o 选项关闭选项:set +o 选项
-x 当执行命令时,打印命令及其参数,类似 bash -x
8 格式化输出 printf
格式

常用格式替换符
替换符
功能
%s
字符串
%f
浮点格式
%b
相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转 义
%c
ASCII字符,即显示对应参数的第一个字符
%d,%i
十进制整数
%o
八进制值
%u
不带正负号的十进制值
%x
十六进制值(a-f)
%X
十六进制值(A-F)
%%
表示%本身
说明:%s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字符宽,- 表示左对齐
常用转义字符
转义符
功能
\a
警告字符,通常为ASCII的BEL字符
\b
后退
\f
换页
\n
换行
\r
回车
\t
水平制表符
\v
垂直制表符
\
表示\本身
实例:
9 算术运算
bash中的算术运算:
+, -, , /, %取模(取余), *(乘方) 乘法符号有些场景中需要转义
实现算术运算:
内建的随机数生成器变量:
范例:
增强型赋值:
10 逻辑运算
范例:
短路运算
短路与
CMD1 短 路 与 CMD2
第一个CMD1结果为 0 (假 ),总的结果必定为0,因此不需要执行CMD2
第一个CMD1结果为 1 (真),第二个CMD2必须要参与运算,才能得到最终的结果
短路或
CMD1 短路或 CMD2
第一个CMD1结果为1 (真),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为0 (假 ),第二个CMD2 必须要参与运算,,才能得到最终的结果
11 条件测试命令
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测 试过程
评估布尔声明,以便用在条件性执行中
若真,则状态码变量 返回 若假, 则状态码变量? 返回1
条件测试命令
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
11.1 变量测试
-v VAR 变量VAR是否设置
示例:判断 NAME 变量是否定义
范例:
11.2 数值测试
范例:
11.3 字符串测试
范例:
11.4 文件测试
存在性测试
范例:
文件属性测试
12 关于() 和 {}
( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境, 帮助参看:man bash 搜索(list) { list; } 不会启子shell, 在当前shell中运行,会影响当前shell环境, 帮助参看:man bash 搜索{ list; } 范例: () 和 {}
13 组合测试条件
第一种方式:
说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持
范例:
第三章 bash的配置文件
bash shell的配置文件很多,可以分成下面类别
1 按生效范围划分两类
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
2 shell登录两种方式分类
2.1 交互式登录
(1)直接通过终端输入账号密码登录
(2)使用“su - UserName” 切换的用户
配置文件执行顺序:
2.2 非交互式登录
(1) su UserName
(2) 图形界面下打开的终端
(3)执行脚本
(4)任何其它的bash实例
执行顺序:
3 按功能划分分类
profile类和bashrc类
3.1 profile类
profile类为交互式登录的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile
功用:
用于定义环境变量
运行命令或脚本
3.2 Bashrc类
bashrc类:为非交互式和交互式登录的shell提供配置
全局:/etc/bashrc
个人:~/.bashrc 功用:
定义命令别名和函数
定义本地变量
4 编辑配置文件生效
修改profile和bashrc文件后需生效两种方法:
重新启动shell进程
source|. 配置文件
范例:
5 Bash 退出任务
保存在~/.bash_logout文件中(用户),在退出登录shell时运行功能:
创建自动备份
清除临时文件
第四章 流程控制
1 条件选择
1.1 选择执行if语句
格式:
单分支
双分支
多分支
说明:
多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句
if 语句可嵌套
范例:
1.2 条件判断case语句
格式:
case支持glob风格的通配符:
2 循环
2.1 循环执行介绍

将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件重复运行次数
循环次数事先已知
循环次数事先未知
常见的循环的命令:for, while, until
2.2 for循环
格式1:
执行机制:
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
for循环列表生成方式:
直接给出列表
整数列表:
返回列表的命令:
使用glob,如:*.sh
变量引用,如:$@,$#
范例: 九九乘法表
格式2
双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的变量操作
I=10;((I++))
说明:
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
范例:
2.3 while循环
格式:
说明:
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控 制变量;而此变量的值会在循环体不断地被修正 进入条件:CONDITION为true 退出条件:CONDITION为false
无限循环
2.4 until循环
格式:
说明:
进入条件: CONDITION 为false
退出条件: CONDITION 为true
无限循环
2.5 循环控制语句continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
格式:
2.5 循环控制语句 break
break [N]:提前结束第N层整个循环,最内层为第1层
格式:
范例:
2.6 循环控制 shift 命令
shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift
范例:
2.7 while read特殊用法
while 循环的特殊用法,遍历文件或文本的每一行
格式:
说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
范例:
2.8 select 循环与菜单
格式:
说明:
select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示符,等待用户输入
用户输入菜单列表中的某个数字,执行相应的命令用户输入被保存在内置变量 REPLY 中
select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按
ctrl+c 退出循环
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list,此时使用位置参量
第五章 函数介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分
函数和shell程序比较相似,区别在于
Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改
1 管理函数
函数由两部分组成:函数名和函数体
帮助参看:help function
1.1 定义函数
1.2 查看函数
1.3 删除函数
格式:
2 函数调用
函数的调用方式
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数 代码
函数的生命周期:被调用时创建,返回时终止
2.1 交互式环境调用函数
交互式环境下定义和使用函数
范例:
2.2 在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数仅使用其函数名即可
2.3 使用函数文件
可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数文件名可任意选取,但最好与相关任务有某种联系,例如:functions
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用delcare -f 或set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
实现函数文件的过程:
创建函数文件,只存放函数的定义
在shell脚本或交互式shell中调用函数文件,格式如下
范例:
3 函数返回值
函数的执行结果返回值:
使用echo等命令进行输出
函数体中调用命令的输出结果
函数的退出状态码:
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回
4 环境函数
类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数定义
环境函数:
查看环境函数:
5 函数参数
函数可以接受参数:
传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量
6 函数变量
变量作用域:
普通变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数
环境变量:当前shell和子shell有效
本地变量:函数的生命周期;函数结束时变量被自动销毁
注意:
如果函数中定义了普通变量,且名称和局部变量相同,则使用本地变量
由于普通变量和局部变量会冲突,建议在函数中只使用本地变量
由于普通变量和局部变量会冲突,建议在函数中只使用本地变量
7 函数递归
函数递归:函数直接或间接调用自身,注意递归层数,可能会陷入死循环递归示例:
阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语,一个正整数的阶乘(factorial)是所有小
于及等于该数的正整数的积,并且有0的阶乘为1,自然数n的阶乘写作n!
n!=1×2×3×...×n
阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!
范例:
fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
函数实现
脚本实现
第六章 其它脚本相关工具
1 信号捕捉 trap
trap '触发指令' 信号
进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作
trap '' 信号
忽略信号的操作
trap '-' 信号
恢复原信号的操作
trap -p
列出自定义信号操作
trap finish EXIT
当脚本退出时,执行finish函数
范例:
2 创建临时文件 mktemp
mktemp 命令用于创建并显示临时文件,可避免冲突
格式:
说明:TEMPLATE: filenameXXX,X至少要出现三个常见选项:
-d 创建临时目录
-p DIR或--tmpdir=DIR 指明临时文件所存放目录位置
范例:
3 安装复制文件 install
install命令格式:
选项:
-m MODE,默认755
-o OWNER
-g GROUP
范例:
4 交互式转化批处理工具 expect
expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率
expect 语法:
常见选项:
-c:从命令行执行expect脚本,默认expect是交互地执行的
-d:可以输出输出调试信息
示例:
expect中相关命令
spawn 启动新的进程
expect 从进程接收字符串
send 用于向进程发送字符串
interact 允许用户交互
exp_continue 匹配多个字符串在执行动作后加此命令
范例:
第七章 数组
1 数组介绍
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引的编号从0开始,属于数值索引
索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持
bash的数组支持稀疏格式(索引不连续)
2 声明数组
注意:两者不可相互转换
3 数组赋值
数组元素的赋值
一次只赋值一个元素
范例:
一次赋值全部元素
范例:
只赋值特定元素
交互式数组值对赋值
范例:
4 显示所有数组
显示所有数组:
5 引用数组
引用数组元素
范例:
引用数组所有元素
范例:
数组的长度,即数组中元素的个数
范例:
6 删除数组
删除数组中的某元素,会导致稀疏格式
删除整个数组
范例:
7 数组数据处理
数组切片:
向数组中追加元素:
范例:
8 关联数组
注意:关联数组必须先声明再调用
范例:
范例:生成10个随机数保存于数组中,并找出其最大值和最小值
第八章 字符串处理
1 字符串切片
基于偏移量取字符串
基于模式取子串
实例:
2 查找替换
3 查找并删除
4 字符大小写转换
第九章 高级变量
1 高级变量赋值

范例:
2 高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是等价的
3 变量间接引用
3.1 eval命令
eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变量进行两次扫描
范例:
3.2 间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为
bash Shell提供了两种格式实现间接变量引用
范例:
最后更新于
这有帮助吗?