介绍编写 shell 脚本的基础知识
更多精彩
- 更多技术博客,请移步 IT人才终生实训与职业进阶平台 - 实训在线
导览
- 可以使用分号在同一行中执行多个命令
- 在创建 shell 脚本时,必须指定 shell 脚本将要使用的 shell 类型,例如
#! /bin/bash
,表示使用 bash shell 作为 shell 脚本的执行环境 - 变量的声明不需要添加任何前缀,但变量的使用需要添加 美元符号( $ ) 作为前缀
- 变量的声明及赋值时,变量名、等号、变量值之间不能有空格
- 可以使用
$()
来实现 命令替换 的效果 - 使用 大于号( > ) 可以实现 输出重定向 ,也就是将命令结果输出到文件
- 使用 双大于号( >> ) 可以在实现 输出重定向 的同时,向已有的文件追加输出结果,而不是替换
- 使用 小于号( < ) 可以实现 输入重定向 ,也就是将文件内容输入到命令
- 使用 双小于号( << ) 以及自定义的标识,可以实现 内联输入重定向 ,也就是不需要实体文件既可以将内容输入到命令
- 可是使用 管道连接 将一个命令的结果输出到另一个命令
- 使用
expr
命令可以实现数学运算,但不是很好用,有些操作符会涉及到 shell 的关键字,需要使用反斜线转义后才能使用 - 使用
$[]
符号也可以实现数学运算,而且操作符不会涉及到 shell 关键字 - 使用
expr
命令或$[]
符号都无法实现浮点运算,但是使用 Linux 内建的bc
计算器可是实现浮点运算 - 输入
bc
可以进入计算器模式,其中的scale
参数可以指定两数相除后的结果精确到小数点后几位 - shell 中的每个命令执行结束后都会返回一个 0-255 之间的 退出状态码 ,使用
echo $?
命令可以输出上一条命令的 退出状态码 - 在
exit
命令后追加数字,可以为 shell 脚本指定一个自定义的 退出状态码
11.1 使用多个命令
- 如果要让两个命令一起运行,可以将其放在同一行,并使用分号分割,如下图
- 这是在第 5 章学过的 命令列表
- 只要 同一行执行的命令不超过 255 个字符 就行
11.2 创建 shell 脚本文件
- 创建 shell 脚本文件,其实就是创建一个普通文件,在文件中放置需要执行的 shell 脚本
- 为了表明这个文件是用来执行 shell 脚本的,需要在文件第一行执行其中脚本需要使用的 shell 类型,比如
#! /bin/bash
就表示文件内的脚本需要使用 bash shell 类执行,如下图- 在 shell 中,
#
号通常被识别为注释的标识,但第一行的#!
例外 - shell 会根据命令出现的顺序依次执行,比如在以下脚本中,
date
命令会先执行,然后再执行who
命令
- 在 shell 中,
11.2.1 执行 shell 脚本
- 创建 shell 脚本之后,下一步就是执行文件,如下图
- 之前创建了 testShell 的 shell 脚本文件,按照我们通常的命令执行经验,执行输入命令名称之后按回车即可
- 但当输入
testShell
命令后,得到的结果是 未找到命令 ,这个时候我们应该想起来第 6 章中学到的关于 PATH 环境变量 的知识- 从 PATH 环境变量 的知识我们可以知道,Linux 在获取命令时默认只会扫描被 PATH 环境变量 覆盖的路径
- 很显然我们执行命令的当前命令不在 PATH 环境变量 中,那么解决方式有哪些?是将当前路径添加到 PATH 环境变量 中吗?
- 很显然将当前这个临时测试使用的命令添加到 PATH 环境变量 中不是一个好的选择
- 应该是 使用绝对路径来执行命令 ,所以应该输入
./testShell
- 但输入
./testShell
命令后,得到的结果是 权限不足 ,这个时候我们应该想起来第 7 章学到的关于 umask 设置默认文件权限 的知识- umask 命令设置的默认文件权限是 022 ,也就是下图中 testShell 显示的 rw-r--r--
-
所以就算是 root 用户,对于该文件默认也只有读写权限,没有执行权限
- 使用
chmod u+x testShell
命令为该文件赋予执行权限后就可以使用./testShell
执行该脚本,效果如下图
11.3 使用 echo 命令显示消息
- 在
echo
命令后面添加一个字符串后再执行命令,这个字符串就会被输出在终端,如下图
- 如果输出的字符串中存在引号,则可以使用单引号和双引号配合的方式输出结果,学过 JS 的对这种操作不会陌生,如下图
- 如果直接输出引号,终端会命令没有结束,所以需要根据实际情况决定是使用单引号还是双引号
-
需要注意的是,如果需要在引号中引用变量,则需要使用双引号包裹,否则变量不会被识别
- 使用
echo -n
命令可以让输出的字符串和下一个命令的输出结果在同一行,如下图
11.4 使用变量
- 在 shell 脚本中也可以使用变量
- 变量的作用是将通过 shell 脚本获取的信息临时的存储在脚本中,当后续脚本需要用到这些信息时,可以通过这些变量进去获取
11.4.1 环境变量
- 在第 6 章学习过系统环境变量,这些变量在 shell 脚本中也是可以直接引用的,使用 美元符号 ( $ ) 即可,如下图
-
左侧是 shell 脚本的内容,右侧是执行结果
-
- 那么如果待输出的字符串中希望直接输出美元符号,而不是作为环境变量的获取符,应该如何操作?如下图
- 可以看到,只需要在美元符号前面加一个 反斜线 ( \ ) 即可
- 可以看到,只需要在美元符号前面加一个 反斜线 ( \ ) 即可
11.4.2 用户变量
- 真正在 shell 脚本中发挥作用的变量肯定还是用户自定义变量
- 用户变量可以是由任何字母、数字或下划线组成的文本字符串
- 单个变量名称的长度 不超过 20 个字符 即可
- 区分大小写,var 和 Var 不是同一个变量
- 变量的声明和赋值方式是
variableName=variableValue
- 变量名、等号、变量值之间不能有等号,否则会报错
- 变量在声明时不需要指定类型,直接声明即可,shell 脚本会自动决定变量值的数据类型
- 这一点说起来就像是 JS 的弱类型特点一样,但其实比 JS 还要简单
- 因为 JS 的变量声明是
var variableName=variableValue
- 而 shell 的变量声明是
variableName=variableValue
,连前置的弱类型标识都省略了
- 声明变量时不需要任何前缀,但引用变量时则需要使用 美元符号( $ ) ,如下图
-
可以看到,如果不使用美元符号作为前缀,变量根本就不会被获取
-
11.4.3 命令替换
- 如果需要将命令的输出赋予变量,需要使用的特性就是 命令替换
- 命令替换 在执行时,实际上不是直接执行,而是开启一个子 shell 来执行需要替换的命令,之后再将结果赋予变量
- 实现 命令替换 的方式两种,如下图
11.5 重定向输入和输出
11.5.1 输出重定向
- 将命令的输出通过 大于号( > ) 发送到指定文件中,就是 输出重定向 ,如下图
-
如果重定向的目标文件已存在,则会直接用新内容覆盖,如下图
- 那么如果不想旧内容被覆盖,则可使用 双大于号( >> ) 进行数据追加,如下图
11.5.2 输入重定向
- 与 输出重定向 相反,将文件中的内容通过 小于号( < ) 输出到命令,就是 输入重定向 ,如下图
- 将 testShell 文件中的内容输出到
sort
命令后,命令执行的结果是对文件中的内容进行了排序后再输出
- 将 testShell 文件中的内容输出到
11.5.2.1 内联输入重定向
- 所谓 内联 ,就是不使用文件,直接在终端将内容输入到命令,如下图
- 可以看到,首先输入符号变成了 双小于号( << )
- 然后 EOF 实际上就是一个起始和结束的标识,两个标识之间被包裹的就是需要被输入到命令的内容
-
标识的命令是完全自定义的,只需要保证首尾的标识是一致的即可被识别
11.6 管道
- 将一个命令的输出重定向到另一个命令,就是 管道连接( Piping ) ,如下图
-
set
命令会将当前系统的所有环境都输出,如果直接输出,通常会一口气显示很多内容 - 现在则是通过
more -10
命令表示将结果使用分页显示,并且一页只显示 10 行 - 而
set
和more -10
之间的 竖线( | ) ,就是用于 管道连接 的符号
-
- 被管道连接的两个命令,并不是依次执行的,而是同时执行
11.7 执行数学运算
- 不论是使用
expr
命令进行运算,还是使用$[]
符号进行运算,shell 脚本原生的运算方式都 只支持整数运算
11.7.1 expr 命令
- 使用
expr
命令可以实现在命令行进行数学运算,但不是很好用,如下图- 两个待运算的值与运算符之间必须使用空格,否则无法被识别
-
有些运算符还涉及到了关键字,导致使用时必须使用反斜线来进行转义,否则会出现语法错误
- 如果在 shell 脚本中使用
expr
命令进行运算的话,还必须使用 命令替换 的格式将epxr
命令的表达式进行包裹,如下图
11.7.2 使用方括号
- 在 shell 脚本中进行数学运算时,可以使用
$[arg1 + arg2]
的方式,如下图- 可以看到,这种方式要比使用
expr
命令方便的多,而且也不会出现运算符是关键字的问题
- 可以看到,这种方式要比使用
11.7.3 浮点的解决方案
- 为了解决 shell 脚本原生的运算方式只支持整数运算的问题,Linux 为 shell 内建了一个名为
bc
的计算器
11.7.3.1 bc 的基本用法
- 输入
bc
命令可以直接进入计算器模式,如下图- 可以看到,在该模式下,输出待计算的公式后按回车即可得到结果
- 而是是支持浮点运算的
- 退出方式则是输入
quit
后再按回车
- 输入
bc -q
命令可以再进入计算器的同时忽略欢迎信息,如下图
- 在计算器模式中,还可以通过
scale
选项设置两数相除后小数点后保留精确度,比如scale=4
就表示保留小数点后四位,如下图-
需要注意的是,只影响除法
-
- 在计算器模式中,不光可以直接进行数学运算,还支持注释、表达式、编程语句以及函数,如下图
- 在计算器中声明了一个变量,再通过
print
命令将变量值输出
- 在计算器中声明了一个变量,再通过
11.7.3.2 在脚本中使用 bc
- 实现的方式就是结合上文中学到的知识点,使用 命令替换 、
echo
命令以及 管道连接 来获取 bc 的计算结果,如下图
- 如果觉得将多个命令及参数都写在同一行不易于理解,还可以结合 内嵌输入重定向 将命令和参数进行换行,如下图
- 需要注意的是,在 bc 计算器中使用的变量,和 shell 脚本不是同一个作用域,所以在 bc 中定义的变量无法在 shell 脚本中引用
11.8 退出脚本
- shell 中运行的每个命令在运行完毕后都会输出一个 退出状态码( Exit Status )
- 退出状态码 是一个 0-255 的整数值,可以在 shell 脚本中捕获命令的退出状态码,从而得知命令的执行结果
11.8.1 查看退出状态码
- 使用
$?
命令可以查看上一个命令执行结束后的 退出状态码 ,如下图- 使用
ls
命令输出当前的目录的内容列表后,再使用$?
命令得到的 退出状态码 是 0 - 使用
nano
命令输出的结果是未找到命令( 因为当前系统没有安装 nano 编辑器 ),再使用$?
命令得到的 退出状态码 是 127
- 使用
- 下图列出一些常见的 退出状态码
11.8.2 exit 命令
- 使用
exit
命令,可以再 shell 脚本结束时指定一个状态码,如下图
-
exit
命令后的数字还可以使用变量值,如下图
- 但是指定的值必须是 0-255 之间的数值,如果超过 255 ,则会使用 指定的值除以 256 之后得到的余数 ,如下图