当前位置:首页 > 资讯 > 正文

Shell编程

  • 将简单的命令组合完成复杂的工作,自动化执行命令,提高工作效率
  • 减少手工命令的输入,一定程度上避免人为错误
  • 将软件或应用的安装及配置实现标准化
  • 用于实现日常性的,重复性的,非交互式的运维工作,如:文件打包压缩备份,监控系统运行状态并实现告警等

shell是基于过程式、解释执行的语言。

shell脚本是包含一些命令或声明,并符合一定格式的文本文件。

一个shell脚本文件中主要包含以下内容

  • 各种系统命令的组合
  • 数据存储:变量、数组
  • 表达式:a + b
  • 控制语句:if
  1. 用编辑器创建新文件,首行必须是 shell 声明(shebang),编辑完成后保存退出

  2. 添加可执行权限

  3. 运行脚本

当前shell执行,和子shell执行

本地执行远程脚本 - 脚本文件在远程主机,在本机执行并在本机生效,执行结果影响的是本机

脚本在本地,我在远程主机执行本地脚本,脚本的输出结果是在本地,但是脚本的作用对象是远程主机

写一个脚本,获取主机系统信息,包括CPU型号,内存大小,硬盘大小,操作系统版本这四个指标

执行前先做语法检查

此选项只能检测脚本中的语法错误,不能检测命令错误,也不会执行脚本

调试并执行,查找命令的错误

逐行输出命令,并输出执行结果

总结:脚本错误常见的有三种

  1. 语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的
  2. 命令错误:默认后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察
  3. 逻辑错误:只能使用 bash -x 进行观察

1、变量类型

变量类型:

  • 内置变量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
  • 用户自定义变量

不同的变量存放的数据不同,决定了以下

  • 数据存储方式
  • 参与的运算
  • 表示的数据范围

变量数据类型:

  • 字符
  • 数值:整型、浮点型,bash 不支持浮点数
  • 布尔
  • 指针
  • 结构体:自定义的复合类型的数据类型
  • ........

2、 Shell中变量命名法则

命名要求

  • 区分大小写
  • 不能使用程序中的保留字和内置变量:如:if, for
  • 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反

命名习惯

  • 见名知义,用英文单词命名,并体现出实际作用,不要用简写,如:ATM
  • 变量名大写
  • 局部变量小写
  • 函数名小写
  • 大驼峰StudentFirstName,由多个单词组成,且每个单词的首字母是大写,其它小写
  • 小驼峰studentFirstName ,由多个单词组成,第一个单词的首字母小写,后续每个单词的首字母
  • 是大写,其它小写
  • 下划线: student_first_name

3、变量的定义和引用

变量的生效范围等标准划分变量类型

  • 普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell 进程均无效
  • 环境变量:生效范围为当前shell进程及其子进程
  • 本地变量:生效范围为当前shell进程中某代码片断,通常指函数
(1)、变量赋值

value 可以是以下多种形式

注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除

(2)、变量引用

弱引用和强引用

  • "$name" 弱引用,其中的变量引用会被替换为变量值
  • '$name' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

变量的间接赋值和引用

变量追加值

在脚本中使用变量

(3)、删除变量
(4)、显示系统中定义的所有变量

4、环境变量

环境变量:

  • 可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
  • 一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
  • 一般只在系统配置文件中使用,在脚本中较少使用

变量声明和赋值:

显示所有环境变量

查看指定进程的环境变量

删除变量

bash内建的环境变量

在父shell中定义的环境变量,在子shell中同样也可以引用

5、只读变量

只读变量:只能声明定义,但后续不能修改和删除,即常量

声明只读变量

查看只读变量

6、位置变量

位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数

$*和$@的区别

$*:将接收到的参数看为一个整体

$@:将接收到每一个参数看作一个单独的参数

范例: 利用软链接实现同一个脚本不同功能

7、退出状态码变量

进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255

用户可以在脚本中使用以下命令自定义退出状态码

注意:

  • 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
  • 如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
  • 如果没有exit命令,即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一 条命令的状态码

8、脚本安全$-

终端中的$- 和 shell脚本中的$- 不同

相当于增强版的 echo, 实现丰富的格式化输出

格式

image-20241125094827824

常用格式替换符

常用转义字符

1、算术运算符

Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩展。求值以固定宽度的整数进行,不检查溢出,尽管除以0 被标记为错误。运算符及其优先级,关联性和值与C语言相同。以下运算符列表分组为等优先级运算符级别。级别按降序排列优先。

注意:bash 只支持整数,不支持小数

乘法符号有些场景中需要转义

实现算术运算

范例:生成 0 - 49 之间随机数

增强型赋值:

i++与++i

2、逻辑运算

image-20241125111431965

true、false

与:&和相与结果为0,和1相与结果保留原值,一假则假,全真才真

或:|和1相或结果为1,和0相或结果保留原值,一真则真,全假才假

异或:

异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得

出另一个值Y

短路运算

短路&&

cmd1&&cmd2

当 cmd1 的结果为 false 时,整个表达式就是 false, cmd2 不参与运算,这就是所谓的短路

当 cmd1 的结果为 true 时,cmd2 才参与运算, 看cmd2的结果是真假,在整体判断 表达式的真假

短路||

cmd1||cmd2

当 cmd1 的结果为 true 时,整个表达式就是 true, cmd2 不参与运算,这就是所谓的短路

当 cmd1 的结果为 false 时,cmd2 参与运算,根据cmd2的结果再去判断整个表达式的真假

条件测试:

判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,

实现评估布尔声明,以便用在条件性环境下进行执行

若真,则状态码变量 $? 返回0

若假,则状态码变量 $? 返回1

条件测试命令

常用选项:文件判断相关

常用选项:字符串判断相关

在比较字符串时,建议放在""中

常用选项:数学相关

常用选项:其它

使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY

管道符左右两侧都是子进程

bash shell的配置文件很多,可以分成下面类别

全局配置:针对所有用户皆有效

个人配置:只针对特定用户有效

1、交互式登录

  • 直接通过终端输入账号密码登录
  • 使用 su - UserName 切换的用户

配置文件生效和执行顺序:

注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序

2、非交互式登录

  • su UserName
  • 图形界面下打开的终端
  • 执行脚本
  • 任何其它的bash实例

执行顺序:

修改profile和bashrc文件后需生效两种方法:

  • 重新启动shell进程
  • source|. 配置文件

注意:source 会在当前shell中执行脚本,所有一般只用于执行置文件,或在脚本中调用另一个脚本的场景

保存在~/.bash_logout文件中(用户),在退出登录shell时运行

功能:

  • 创建自动备份
  • 清除临时文件

image-20241126162439949

image-20241126162455880

image-20241126162511888

1、if语法

2、case语句

将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件

重复运行次数

  • 循环次数事先已知
  • 循环次数事先未知

常见的循环的命令:for, while, until

image-20241129105125888

1、for循环

执行机制:

  • 依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
  • 如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"

for 循环列表生成方式:

  • 直接给出列表
  • 整数列表:如
  • 返回列表的命令:如 $(COMMAND)
  • 使用glob,如:*.sh
  • 变量引用,如:$@,$*,$#

有若干只兔和鸡,兔和鸡加起来一共有100条腿,请写一个简单的shell算出兔和鸡各多少只可能组合(假设所有的鸡和兔子的腿都是健全的,且兔和鸡至少为1只)

2、while循环

说明:

CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为

“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控

制变量;而此变量的值会在循环体不断地被修正

进入条件:CONDITION为 true

退出条件:CONDITION为 false

国际象棋棋盘

while特殊用法,while read

while 循环的特殊用法,遍历文件或文本的每一行

3、continue和break

continue:结束本次循环,进入下一次循环

break:结束当前循环,跳出当前的循环体

函数 function

是由若干条shell命令组成的语句块,实现代码重用和模块化编程

它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分

函数和shell程序区别

  • Shell程序在子Shell中运行
  • 函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改

函数的调用方式

  • 可在交互式环境下定义函数
  • 可将函数放在脚本文件中作为它的一部分
  • 可放在只包含函数的单独文件中

调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码

函数的生命周期:被调用时创建,返回时终止

return

函数默认返回值是函数体中最后一条命令的退出状态码;

也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行

return和exit的区别

return 只会中断函数执行,但exit 会中断整个脚本的执行

函数可以接受参数:

  • 传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
  • 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

变量作用域

在函数中定义本地变量

函数递归:

函数直接或间接调用自身,注意递归层数,可能会陷入死循环

递归特点:

  • 函数内部自已调用自已
  • 必须有结束函数的出口语句,防止死循环

fork炸弹

fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序。

由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源

trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能

在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这种情况,我们可以使用信号捕捉,来自定义信号处理。

显示所有的信号

信号的3种表示方法

mktemp 命令用于创建并显示临时文件,可避免冲突

install 功能相当于cp,chmod,chown,chgrp ,mkdir 等相关工具的集合

变量:存储单个元素的内存空间

数组:存储多个元素的连续的内存空间,相当于多个变量的集合

数组名和索引

  • 索引的编号从0开始,属于数值索引
  • 索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持
  • bash的数组支持稀疏格式(索引不连续)

两者不能相互转换

删除数组中的某元素,会导致稀疏格式

删除整个数组

1、数组切片

2、数组追加元素

最新文章