shell 脚本调试技巧

我们掌握了编写脚本的基本理论后,脚本越写越复杂,犯点错误在所难免。当遇到问题的时候不用怕,使用这接下来要讲的调试必杀技帮你找到并解决问题!

1.1.1             空变量问题

举个简单的例子,见代码6:

代码6:

#!/bin/sh

num=1

if [ $num= "1" ]; then

       echo "Number is 1"

else

       echo "Number is not 1"

fi

这是一个正确的脚本。如果我们把第3行从“num=1”变成“num=”,看看会发生什么?运行改后的脚本,你会得到以下结果:

./tt: line 5: [: =: unary operatorexpected

Number is not 1

你发现了错误信息提示是“line 5: [: =: unary operator expected”。明明我改了第3行,但是为什么提示错误发生在第5行,与“[”这个有关呢?莫着急,我来给你解释一下。

“num=” 语法上是没有错误的。你可以赋给变量为空。这个问题与shell替换文本有关。在第5行,当shell看到“num”变量,就替换它了。如果“num=1”就变成:

if [ 1 = "1" ]; then

可是当“num=”的时候,shell替换就变成:

if [ = "1" ]; then

你能说这不是错误吗?“=”是一个二进制运算符,左右两边都应该有东西。Shell试图告诉我们,只存在一个东西,那就要用unary 运算符(例如“!”),这种运算符支持单项。

为了改正这个错误,修改第5行,把它变成:

if [ "$num" ="1" ]; then

这样shell的替换就会变成:

if [ "" = "1"]; then

这就正确地表达了我们的意图。这个小例子告诉我们要注意当变量为空的情况。

1.1.2             缺引号问题

在代码6中我们去掉第6行的引号,

echo "Number equals 1

这次我们又得到什么了?

./tt: line 6: unexpected EOF whilelooking for matching `"'

./tt: line 8: syntax error:unexpected end of file

在某一行的错误会导致在脚本后面几行发生错误。Shell不断在寻找字符串结束的引号,到文件结束时也没有找全。在很长的脚本文件中遇到这种错误有时候会非常郁闷。所以在你加少量新代码的时候就开始测试,一点一点加,一点一点测,省得到最后再找问题,麻烦就大了。如果在编辑器中采用语法高亮的功能,这种类似缺匹配语法符号的问题就比较容易发现了。

1.1.3             隔离问题

大千世界无奇不有,错误也千奇百怪,找到bug原因有时候可能很困难,下面给你支几招。

注释掉一块代码看看出现的问题“跑”了没有。例如上缺引号的问题,如果把else那部分代码注释掉了,

#else

#     echo"Number does not equal 1"

运行后问题还在。我们就可以肯定问题不在else这段代码中,尽管错误提示在这行。

1.1.4             echo-普通中见“伟大”

使用echo命令放在你心存疑虑的地方,是个不错的方法。我们在开发过程中经常发现bug不在我们第一次感觉它在的地方。为了解决这个问题,在你调试程序过程中,可以使用echo命令去证实你的猜想。你可以插入两种信息。

第一种就是在程序的某个点插入一句描述,看看程序是否像我们想象得那样到此一游过。

第二种就是显示计算或测试的变量值。你会发现某段程序失败,经常是因为我们开始假想它是正确的,实际上不正确,导致程序后来失败。

1.1.5             -x跟踪问题本领高

加入“-x”在你运行的脚本第一行:

#!/bin/sh -x

我们再来运行脚本上面的脚本,shell会会打印出每个命令执行的结果。这个技术叫跟踪。这个简单的例子在-x后所得结果如下:

+ num=1

+ '[' 1 = 1 ']'

+ echo 'Number is 1'

Number is 1

你也可以不用在脚本中加入-x,而是用sh -x scriptname执行命令,这两者是等价的。另外还有几种方法运行脚本,如:sh -n scriptname不会运行脚本, 只会检查脚本的语法错误;sh -v scriptname将会在运行脚本之前, 打印出每一个命令。-n和-v可以同时使用,做详细的语法检查。

1.1.6             assert

许多软件在编程思路上借鉴C语言,“assert”(断言)函数就是其中之一。Shell使用“assert”函数在临界点上测试变量或条件。我们认为在运行时该条件应该为真,当遇到假的情况,就不能继续执行脚本,而是立刻对相关错误进行处理。

例如:

assert"$condition" $LINENO

# 脚本以下的代码只有当"assert"成功时才会继续执行。其中$LINENO是用来显示行号的。

1.1.7             caller

caller[framenumber]是内建命令。在函数中里使用caller命令用于在标准输出中打印这个函数的相关信息。如果没有给framenumber或framenumber为0意味着打印最顶层执行的frame信息,此信息包括行号,谁调用了这个函数和文件名。请看代码7这个trycaller脚本:

代码7:

#!/bin/sh

function1()

{

       caller 0  # 告诉我相关信息

}

function1

caller 0    #不在函数中的caller不起作用。

执行结果:

bogon:~ #./trycaller

6 main./trycaller

在上面例子中虽然有两个caller,但却只打印了一行信息,是因为caller放在函数中才能起作用,在脚本主体中不起作用。

1.1.8             trap

trap命令用于指定在接收到信号后将要采取的行动。语法是一般是这样的:

trap[COMMANDS] [SIGNALS]

SIGNALS为带有SIG前缀的信号标识,或者干脆就是数字也行。具体这个信号都有啥,本书的第二章就介绍过了。如果你懒得往回翻,可以使用命令“man 7signal”查看。

为了便于理解,我们来看一个实际的例子。代码8这个脚本捕捉“Ctrl+c”组合键发出的信号SIGINT后输出临别赠言,并删除临时文件:

代码8:

#!/bin/sh

 

control_c()

# 如果用户键入control-c就执行

{

  echo -en "\n*** 亲,我要走了。祝幸福!***\n"

  rm -f /tmp/tempfile

  exit $?

}

 

# 设置control-c键盘中断

trapcontrol_c SIGINT

 

# main 循环

whiletrue; do read x; done

trap命令还有两个特别的“信号”,分别是DEBUG和ERR。它们的特别之处就在于其根本就是不是信号,而是trap的模式开关。如果指定为DEBUG模式,那么在每执行一个命令后都会执行COMMANDS;而如果指定为ERR模式,那么只要有命令以非零状态退出就会执行COMMANDS。代码9给出了DEBUG模式的例子,至于ERR模式的例子就当是作业吧!

代码9:

#!/bin/bash

#在每个命令行显示变量$variable的值.

trap'echo "VARIABLE-TRACE> \$variable = \"$variable\""'DEBUG

 

variable=29

echo"Just initialized \"\$variable\" to $variable."

let"variable *= 3"

echo"Just multiplied \"\$variable\" by 3."

 

# $? 指的是前一个命令的返回码

exit $?

 

执行上面的脚本trytrap后,每个命令行上都会显示$variable的值帮助我们跟踪查看。

bogon:~ #./trytrap

VARIABLE-TRACE>$variable = ""

VARIABLE-TRACE>$variable = "29"

Justinitialized "$variable" to 29.

VARIABLE-TRACE>$variable = "29"

VARIABLE-TRACE>$variable = "87"

Justmultiplied "$variable" by 3.

VARIABLE-TRACE>$variable = "87"

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值