Shell 编程基础(三)
编程基础
解释器:
Linux 提供 shell 解析器 | 注:Centos 默认的解析器是 bash
1 | $ cat /etc/shells |
Shell 脚本第一行指定解释器必须写 #!/bin/bash
, 如果没有会默认使用 #!/bin/sh
作为解释程序
注释:
Shell 注释,单行 #
, 多行
1 | <<COMMENT |
设置执行权限
当前用户增加执行权限 chmod u+x ./脚本名.sh
所有用户增加执行权限 chmod +x ./脚本名.sh
将脚本路径添加到环境变量中,可以实现任意位置调用,不用使用全路径
Bash 中的参数扩展
1 | #参数名后面还紧连着其他字符,必须使用{} |
Bash 的内部变量
1 | 些常见的 Bash 内部变量 |
Bash 中的位置参数和特殊参数
- Bash 中的位置参数是由除 0 以外的一个或多个数字表示的参数。
多于一个数字的位置参数在扩展时必须放在大括号中。比如,位置参数 10 在扩展时使用${10}。
Bash 对一些参数的处理比较特殊。这些参数只能被引用,但不能修改它们的值。这些特殊参数分别是
*
、@
、#
、?
、-
、$
、!
、0
和_
。特殊参数@,也将扩展为从 1 开始的所有位置参数。但当它的扩展发生在双引号内时,每个参数都扩展为分隔的单词。也就是说,“$@”等价于“$ 1”、“$2”…。参数@与*之间的区别将在 for 循环的调用中显现出来。
特殊参数#,将扩展为位置参数的个数,用十进制表示:
1
2
3
4
5
6
7
8
9
10
11set one two three # 设置位置参数
echo $#
3特殊参数
?
,将扩展为最近一个在前台执行的命令的退出状态。以使用它来 检查 Shell 脚本是否已成功地执行,通常退出状态 0 表示命令已经没有任何错误地结束运行。特殊参数
-
,将扩展为当前的选项标志。这些选项是在调用时,或由内部命令 set 指定,或由 Shell 自身指定。特殊参数
$
,将扩展为当前 Shell 的进程号。在一个子 Shell 中,它扩展为调用 Shell 的进程号,而不是子 Shell 的进程号。特殊参数
!
,将扩展为最近一次执行的后台命令的进程号特殊参数
0
,将扩展为 Shell 或 Shell 脚本的名称。它是在 Shell 初始化时设置。特殊参数
_
,在 Shell 启动时,它被设为开始运行的 Shell 或 Shell 脚本的路径。
用 declare 指定变量的类型
declare
命令是Bash
的内部命令,用于声明变量和修改变量的属性。它与Bash
的另一个内部命令typeset
的用法和用途完全相同。1
2
3
4
5
6
7#如果直接使用declare命令,不指定变量名,将显示所有变量的值
declare
#使用-r选项,declare命令将把指定的变量定义为只读变量,这些变量将不能再被赋予新值或被清除
declare -r var=1
使用-i选项,declare命令将把指定的变量定义为整数型变量,赋予整数型变量的任何类型的值都将被转换成整数
使用-x选项,declare命令将把指定的变量通过环境输出到后续命令
使用-p选项,declare命令将显示指定变量的属性和值
Bash 中的数组变量
1 | 接声明一个数组变量的语法如下所示: |
Bash 的算术运算符
Bash 中的算术运算符以及它们的优先级、结合性和值都与 C 语言相同。
![](3-Shell 编程基础/b81907185151424d331c9a8474db9e64.png)
![](3-Shell 编程基础/9b645f167ce2cf7afca44434d68c3af6.png)1
2
3
4
5
6
7
8#求幂运算符**
let var=5**2
echo $var
> 25
#逗号运算符将两个或更多的算术运算连接在一起,所有的运算都被求值,但只有最后一个运算的值被返回。
let var=(2+3, 10-5, 20-6)
echo $var
14
数字常量
默认情况下,Shell 算术表达式都是使用十进制数,除非这个数字有特定的前缀或标记。以 0 开头的常量将被当作八进制数解释,而以“0x”或“0X”开头的数值将被解释为十六进制数。此外,如果数值的格式是 BASE#NUMBER,BASE 是介于 2~64 之间的十进制数,表示算术进制基数,比如,BASE 是数字 12,那么 12#NUMBER 就表示十二进制数,NUMBER 即为此进制中的数值。
1
2
3
4
5let dec=20 #默认为十进制数
let oct=020 #以0 开头的八进制数
let hex=0x20 #以0x 开头的十六进制数
let bin=2#111 #符号“#”之前的数字 2 表示此数值为二进制
let base32=32#20 #三十二进制数,数值为 20
使用算术扩展和 let 进行算术运算
算术扩展中的运算数只能是整数,算术扩展不能对浮点数进行算术运算。
1 | ``` |
使用 expr 命令
expr
命令是一个用于对表达式进行求值并输出相应结果的命令行工具。它同样也只支持整数运算数,不支持浮点运算数的运算。与
let
命令相反,使用expr
命令时,表达式中的运算符左右必须包含空格,如果没有空格,而是将运算符与运算数直接相连,expr
命令将不会对表达式进行求值,而直接输出算术表达式。使用
expr
命令时,对于某些运算符,还需要使用符号“\”进行转义,否则提示语法错误。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16expr 6 + 8
14
expr 6+8 #运算符左右没有包含空格
6+8
expr 6 * 8 #乘法符号需要使用符号“\”进行转义
expr: syntax error
expr 6 \* 8
48
expr 1 \< 2 #运算符“<”同样需要转义
1
expr 2 \> 5 #运算符“>”同样需要转义,还有运算符“<=”、“> =”、“|”和“&”
a=15
b=35
expr $a \* $b
525
c='expr $a \* $b' #使用命令替换对变量进行赋值
脚本常识
退出脚本
- 当它运行完成时,应当返回一个 退出状态,用于标识脚本是否成功运行。
- 退出状态码,每一个命令都会返回一个退出状态。一个运行成功的命令会 返回一个 0。不成功返回一个 错误状态码。
使用 exit 命令
exit N
exit 命令语句用于从 Shell 脚本中退出并返回指定的退出状态码 N,来指示 Shell 脚本是否成功结束。- 如果退出状态码 N 被省略,则将把最后一条运行的命令, 的退出状态作为脚本的退出状态码
强烈建议,在你的 Shell 脚本中对调用的程序进行退出状态检查,并根据退出状态做出相应的处理,当脚本退出运行时,明确地返回一个退出状态码,这对一个完善的 Shell 脚本来说,是不可或缺的。
调试脚本
Shell 脚本调试的主要工作是发现引发脚本错误的原因,以及在脚本中定位发生错误的行。
常用方法,使用 Bash 的
-x
选项启动一个 Shell,Shell 在执行脚本的过程中把实际执行的每一个命令行显示出来,并且在命令行的行首显示一个“+”
号,“+”
号后面显示的是经过了参数扩展之后的命令行的内容,有助于分析实际执行的是什么命令。1
2
3
4
5
6
7
8
9bash -x param_underscore.sh
+ echo 'The $_is /bin/bash'
The $_ is /bin/bash
+ uname -a
Linux localhost 2.6.18-238.9.1.el5PAE #1 SMP Tue
Apr 12 19:28:32 EDT 2011 i686 i686 i386 GNU/Linux
+ echo -a
-a
#上面的输出结果中,前面有“+”号的行是 Shell 脚本实际执行的命令,其他行则是 Shell 脚本的打印输出信息。
这一调试功能在自 3.0 以后的 Bash 大多数现代版本中可用。
1 | ``` |
Bash 中还有一个
“-v”
选项,该选项将激活详细输出模式,在这一模式中,由 Bash 读入的脚本的每一个命令行都将在执行前被打印输出。比如使用-v
选项运行脚本param_ underscore.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23bash -v param_underscore.sh
#通常,将-v 选项和-x 选项同时使用,可以得到更为详细的脚本调试信息
#x选项虽然使用起来比较方便,但它输出的调试信息仅限于参数扩展之后的每一条实际执行的命令以及行首的一个“+”
# -x 选项输出的信息只限于参数扩展之后的每一条实际执行的命令以及行首的一个+号,没有代码行号
#用的 Bash 内部环境变量
$LINENO:表示Shell脚本的当前行号。
$FUNCNAME:它是一个包含了当前在执行调用堆栈中的所有Shell函数名称的数组变量。
${FUNCNAME[0]}代表当前正在执行的Shell函数的名称,${FUNCNAME[1]}则代表调用函数${FUNCNAME[0]}的函数的名字
$PS4:我们在前面已经讲到,使用Bash的-x选项时,每一条实际执行的命令的行首会显示一个“+”号,而这个“+”号其实就是变量$PS4的默认值。
#增强调试:通过重新定义变量$PS4,就可以增强-x 选项的输出信息。例如我们先在命令行提示符下执行如下语句:
export PS4='{$LINENO:${FUNCNAME[0]}}'
# 然后再使用 Bash 的-xv 选项来调试脚本 param_underscore.sh:
bash -xv param_underscore.sh
# Bash 中还有一个执行选项-n,它可用于测试 Shell 脚本中是否存在语法错误,它会读取脚本中的命令但不会执行它们。在编写完 Shell 脚本后,实际执行之前,最好首先使用-n 选项来测试脚本中是否存在语法错误,这是一个好的习惯。因为
bash -n 脚本名.sh #
本编程风格
- 一行不超过 80 字符
- 保持一致的缩进深度。
- 每个脚本文件都要有注释
- 自定义的变量名或函数名使用小写字母,使用下划线“_”分隔单词
自定义变量
基本语法
1 | 定义变量:变量=值 |
注:静态变量不能 unset
等号两侧不能有空格
在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算
变量的值如果有空格,需要使用双引号或单引号括起来
1 | A=5 //定义 |
特殊变量
- $n:n为数字,$ 0 代表该脚本名称,$1-$ 9 代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10})
- $#:获取所有输入参数个数,常用于循环
- $ * :这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体
- $ @:这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
- $?: 最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
运算符
基本语法
1 | $((运算式)) $[运算式] |
| 注:expr 运算符之间要有空格
1 | expr 2 + 3 //简单计算 |
条件判断
基本语法
1 | 1. [ condition ] |
实例
1 | [ 23 -ge 22 ] //条件 |
流程控制
if 判断
1 | if [ 条件判断式 ];then |
注意事项:
(1)[ 条件判断式 ],中括号和条件判断式之间必须有空格
(2)if 后要有空格
1 | //前面$表示用户权限 不是命令内容 |
case
基本语法
1 | case $变量名 in |
注意事项:
- case 行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。
- 双分号“;;”表示命令序列结束,相当于 java 中的 break。
- 最后的“*)”表示默认模式,相当于 java 中的 default。
for
基本语法
1 | for (( 初始值;循环控制条件;变量变化 )) |
例
1 | $ touch for1.sh //创建脚本 |
第二种 for 循环
1 | for 变量 in 值1 值2 值3… |
例
1 | $ touch for2.sh //看不懂别学了 |
- $*和$@都表示传递给函数或脚本的所有参数,不被双引号“”包含时,都以 $1 $ 2 …$n 的形式输出所有参数
- 当它们被双引号“”包含时,“$*”会将所有的参数作为一个整体,以“$ 1 $2 …$ n”的形式输出所有参数;“$@”会将各个参数分开,以“$ 1” “$2”…”$ n”的形式输出所有参数
while 循环
基本语法
1 | while [ 条件判断式 ] |
无需实例
第一个 Shell 脚本
第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限
1 | 1. 创建一个sh脚本文件 |