D12

1. 退出脚本

Shell脚本运行完成,应当返回一个退出状态,用于标识脚本是否成功运行

1)退出状态码

运行成功的命令返回0;不成功的命令返回非0。

在shell脚本或函数中,最后执行的一条命令决定其退出状态(可以用$?检查)。

检查命令或脚本状态是很重要的:

$ cd test #当这条语句执行失败时,执行下一条语句是非常危险的
-bash: cd: test: No such file or directory
$ rm -rf *  #必须先检查前一条命令的状态

 

2)使用exit命令

exit命令用于从Shell脚本中退出并返回指定的退出状态码N,来指示Shell脚本是否成功结束。

语法: exit N #N必须是一个介于0~255之间的整数。

$ cat rmtest.sh 
#!/bin/bash
#2020-05-19

cd ./test

if [ $? -eq 0 ];then #检查cd命令是否运行成功,如果成功才运行rm命令
rm -rf *
else
echo "Cannot change directory!" #如果cd命令运行失败,则打印一个错误信息,并退出,返回退出码1
exit 1

fi

强烈建议:脚本对调用的程序进行退出状态检查,并根据退出状态做出响应的处理。当脚本退出时,明确地返回一个退出状态码,这对一个完善的Shell脚本来说,是不可或缺的。

 

2. 调试脚本

Shell脚本调试的主要工作是发现引发脚本错误的原因,以及在脚本中定位发生错误的行。

Bash提供了多种脚本调试的功能,但最常用的是-x选项启动一个子Shell,它将以调试模式运行整个脚本,使Shell在执行脚本的过程中把实际执行的每一个命令行显示出来,并且在命令行的行首显示一个+(加号),后面显示的是经过了参数扩展之后的命令行的内容,有助于分析实际执行的是什么命令。

$ sh -x para_underscore.sh #使用-x选项以调试模式运行脚本
+ echo '$_ is: /usr/bin/sh'
$_ is: /usr/bin/sh
+ ls -l ./
total 64
-rwxrwxr-x. 1 user1 user1    49 May 18 14:22 childprocess.sh
-rw-rw-r--. 1 user1 user1    14 May 15 10:36 d.txt
... #省略内容
+ echo 'New $_ is: ./'
New $_ is: ./

可以使用set命令设置Bash的执行选项

$ uname -a
Linux ... GNU/Linux #省略内容

$ set -x #启动调试选项
$ uname -a
+ uname -a
Linux ... GNU/Linux #省略内容

$ set +x #关闭调试选项
+ set +x
$ uname -a
Linux ... GNU/Linux #省略内容

 

Bash还有-v选项,可激活详细输出模式,此时Bash读入的脚本的每一个命令行都将在执行前被打印输出。

$ sh -v para_underscore.sh #使用-v选项,详细输出
#!/bin/bash

echo "\$_ is: $_"
$_ is: /usr/bin/sh

ls -l ./
total 64
-rwxrwxr-x. 1 user1 user1    49 May 18 14:22 childprocess.sh
-rw-rw-r--. 1 user1 user1    14 May 15 10:36 d.txt
... #省略内容

echo "New \$_ is: $_"
New $_ is: ./

 

通常,同时使用-v和-x选项,得到更为详细的脚本调试信息。

$ sh -vx para_underscore.sh #同时使用-v和-x选项
#!/bin/bash

echo "\$_ is: $_"
+ echo '$_ is: /usr/bin/sh'
$_ is: /usr/bin/sh

ls -l ./
+ ls -l ./
total 64
-rwxrwxr-x. 1 user1 user1    49 May 18 14:22 childprocess.sh
-rw-rw-r--. 1 user1 user1    14 May 15 10:36 d.txt
... #省略内容

echo "New \$_ is: $_"
+ echo 'New $_ is: ./'
New $_ is: ./

 

对于-x选项,没有输出行号等重要调试信息,不方便。

Bash的一些内部环境变量可以增强-x选项的输出信息。

$LINENO:表示Shell脚本的当前行号。

$FUNCNAME:包含了当前在执行调用堆栈中的所有Shell函数名称的数组变量,${FUNCNAME[0]}为当前正在执行的Shell函数的名称,${FUNCNAME[1]}为调用${FUNCNAME[0]}的Shell函数的名称,一次类推。

$PS4:使用-x选项,前面的+就是该参数的默认值,可以重新定义该变量以增加调试信息。

$ export PS4='+{$LINENO:${FUNCNAME[0]}}' #重新定义$PS4,增加行号、函数名称数组变量中的当前函数名称


$ sh -vx para_underscore.sh #使用-v和-x选项执行
#!/bin/bash

echo "\$_ is: $_"
+{3:}echo '$_ is: /usr/bin/sh' #注意:此处增加了行号和函数名称(当前无,所以为空)
$_ is: /usr/bin/sh

ls -l ./
+{5:}ls -l ./
total 64
-rwxrwxr-x. 1 user1 ntrade    49 May 18 14:22 childprocess.sh
-rw-rw-r--. 1 user1 user1 14 May 15 10:36 d.txt ... #省略数据 echo "New \$_ is: $_" +{7:}echo 'New $_ is: ./' New $_ is: ./

Bash中还有其他的内置变量,可以通过man bash查看,使用这些变量重新定义$PS4可以有针对性的调试程序。

 

Bash中还有一个选项-n,可以检查脚本中的语法错误。编写脚本后,实际执行前,建议首先使用-n来测试脚本。是一个好习惯。

$ cat -n para_underscore.sh
     1  #!/bin/bash
     2
     3  echo \$_ is: $_" #这里故意删除一个双引号,制造错误
     4
     5  ls -l ./
     6
     7  echo "New \$_ is: $_"
     
$ bash -n para_underscore.sh 
para_underscore.sh: line 7: unexpected EOF while looking for matching `"' #会发现错误
para_underscore.sh: line 8: syntax error: unexpected end of file

 

D13

3. Shell脚本编程风格

Shell脚本需要维护和扩展,需要使用良好的编程风格,使脚本容易被理解和修改。

基本的编程风格要求:

1)每个代码行不多于80个字符。

2)保持一致的缩进深度。

程序结构的缩进应与逻辑嵌套深度保持一致。

在每一个代码块之间留一个空行,可以提高脚本的可读性。

3)每个脚本文件必须要有一个文件头注释(文件头提供文件名和它的内容等一些信息),

任何一个不简短且不显而易见的函数都需要注释,

脚本中任何复杂的、不是显而易见的以及重要的代码部分都需要注释。

4)自定义的变量名或函数名使用小写字母,使用下划线“_”分隔单词。

5)程序和脚本的返回值需要使用变量$?进行验证。