Focus On Oracle

Installing, Backup & Recovery, Performance Tuning,
Troubleshooting, Upgrading, Patching, Zero-Downtime Upgrade, GoldenGate

Oracle Exadata ,Oracle ODA, Oracle ZDLRA


当前位置: 首页 » 技术文章 » Oracle

如何Debug Shell脚本

作为一名优秀的系统管理员或开发者,熟练掌握shell编程是必备的技术,作为Oracle DBA,编写Shell脚本也是不可或缺的。日常工作中,我们调试脚本主要是找出脚本错误的原因及定位在脚本源代码中发生错误的行。通过分析输出的错误信息,在脚本中加入调试语句等方法,可以帮助我们去解决问题。本文是基于调试oracle-rdbms-server-12cR1-preinstall-verify写的。

执行oracle-rdbms-server-12cR1-preinstall-verify时,会出现以下错误提示
[root@db1 ~]# oracle-rdbms-server-12cR1-preinstall-verify
/bin/sed: -e expression #1, char 116: unknown command: `3'
/bin/sed: -e expression #1, char 116: unknown command: `3'
[root@db1 ~]#

如何调试一个shell脚本?以这个脚本为例,大致讨论以下几种方法:
1.可以在脚本里设置
 把#!/bin/bash替换为
   #!/bin/bash -xv

  [root@db1 ]# head -5 /usr/bin/oracle-rdbms-server-12cR1-preinstall-verify
  #!/bin/bash -vx
  ##########################################################
  #
  #  Copyright (c) 2014, Oracle. All rights reserved
  #
  [root@db1 ]# oracle-rdbms-server-12cR1-preinstall-verify
  [root@db1 ]# oracle-rdbms-server-12cR1-preinstall-verify
  module () {  eval `/usr/bin/modulecmd bash $*`
  }
  #!/bin/bash -vx
  ##########################################################
  #
  #
  省略中间输出
  /bin/cp ${PRE_PARAM_LOG} ${BACKUP_DIR}
  + /bin/cp /var/log/oracle-rdbms-server-12cR1-preinstall/results/orakernel.log /var/log/oracle-rdbms-server-12cR1-preinstall/backup/Jun-17-2016-17-01-04
  /bin/rm -f ${OLD_PARAMS}
  + /bin/rm -f /var/log/oracle-rdbms-server-12cR1-preinstall/results/.oracle-rdbms-server-12cR1-preinstall.param
  exit 0
  + exit 0

2.执行脚本时带调试选项
  [root@db1 ]# bash -n oracle-rdbms-server-12cR1-preinstall-verify

  [root@db1 ]#

  -n:只读取shell脚本不执行,查语法问题
  -v:执行脚本之前,先把内容输出到屏幕上
  -x:执行脚本,进入跟踪方式,显示所执行的每一条命令(将脚本及变量的值输出到屏幕上)

  [root@db1 ]# bash -x oracle-rdbms-server-12cR1-preinstall-verify
  + LANG=C
  ++ /usr/bin/readlink oracle-rdbms-server-12cR1-preinstall-verify
  + PWD=
  ++ /usr/bin/dirname oracle-rdbms-server-12cR1-preinstall-verify
  + PWD=.
  ++ cd .
  ++ /bin/pwd
  + BASE_DIR=/root
  + TEST_LOG_LOC=/var/log/oracle-rdbms-server-12cR1-preinstall/results
  + CONFIG_FILE=/root/oracle-rdbms-server-12cR1-preinstall.conf
  + PARAMS=/root/oracle-rdbms-server-12cR1-preinstall.param
  + OLD_PARAMS=/var/log/oracle-rdbms-server-12cR1-preinstall/results/.oracle-rdbms-server-12cR1-preinstall.param 

3.设置调试选项
  set -x
  set -n
  set -v
  set -vx
  可以通过下面的命令取消调试选项
  set +x
  set +v
  [root@db1 ~]# set -vx
  printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
  printf '\033]0;%s@%s:%s\007' root db1 '~'
  [root@db1 ~]# oracle-rdbms-server-12cR1-preinstall-verify
  oracle-rdbms-server-12cR1-preinstall-verify
  oracle-rdbms-server-12cR1-preinstall-verify
  /bin/sed: -e expression #1, char 116: unknown command: `3'
  /bin/sed: -e expression #1, char 116: unknown command: `3'
  printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
  printf '\033]0;%s@%s:%s\007' root db1 '~'
  [root@db1 ~]#
[root@db1 ~]# set +x
set +x
set +x
printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
[root@db1 ~]# set +v
set +v
[root@db1 ~]#
[root@db1 ~]# pwd
/root
[root@db1 ~]#

4.使用trap
把下面一行
trap 'echo current line is:$LINENO' DEBUG
加入到文件/usr/bin/oracle-rdbms-server-12cR1-preinstall-verify脚本开始位置,然后执行
[root@db1 ~]# oracle-rdbms-server-12cR1-preinstall-verify
current line is:27
current line is:28
current line is:29
current line is:30
current line is:31
current line is:32
current line is:33
current line is:34
current line is:35
current line is:36
current line is:37
current line is:38
current line is:39
current line is:40
current line is:41
current line is:43
current line is:44
current line is:46
current line is:47
current line is:48
current line is:49
current line is:49
current line is:49
current line is:50
current line is:51
current line is:56
current line is:57
current line is:58
current line is:949
current line is:950
current line is:951
current line is:959
current line is:961
current line is:972
current line is:973
current line is:973
current line is:975
current line is:978
current line is:979
current line is:983
current line is:984
current line is:988
current line is:989
current line is:993
current line is:994
current line is:997
current line is:998
current line is:999
/bin/sed: -e expression #1, char 116: unknown command: `3'
/bin/sed: -e expression #1, char 116: unknown command: `3'
current line is:1000
current line is:1004
current line is:1005
current line is:1008
current line is:1009
current line is:1013
current line is:1014
current line is:1017
current line is:1018
current line is:1022
current line is:1023
current line is:1046
current line is:1052
current line is:1059
current line is:1061
current line is:1062
current line is:1063

[root@db1 ~]#

错误输出在999-1000行之间
current line is:999
/bin/sed: -e expression #1, char 116: unknown command: `3'
/bin/sed: -e expression #1, char 116: unknown command: `3'

current line is:1000

oracle-rdbms-server-12cR1-preinstall-verify脚本的999-1003行内容为:
    999 f_checkuserinfo ;
   1000 if [ $? -ne 0 ]; then
   1001   echo "Verifying of certain user limits failed. " >> ${PRE_PARAM_LOG}
   1002   echo "" >> ${PRE_PARAM_LOG}
   1003 else
可以看到是函数f_checkuserinfo的问题,这个函数的定义在前面,要知道问题代码发生的具体行号,还需要再仔细查看函数f_checkuserinfo的内容。

5.使用"-x"选项的增强
$LINENO
代表shell脚本的当前行号,类似于C语言中的内置宏__LINE__

$FUNCNAME
函数的名字,类似于C语言中的内置宏__func__,宏__func__只能代表当前所在的函数名,$FUNCNAME的功能很强大,它是一个数组变量。{FUNCNAME[0]}代表shell脚本当前正在执行的函数的名字,而变量${FUNCNAME[1]}则代表调用函数${FUNCNAME[0]}的函数的名字

$PS4
主提示符变量$PS1和第二级提示符变量$PS2比较常见,但很少有人注意到第四级提示符变量$PS4的作用。我们知道使用“-x”执行选项将会显示shell脚本中每一条实际执行过的命令,而$PS4的值将被显示在"-x"选项输出的每一条命令的前面。在Bash Shell中,缺省的$PS4的值是"+"号。(现在知道为什么使用"-x"选项时,输出的命令前面有一个"+"号了吧?)。

利用$PS4这一特性,通过使用一些内置变量来重定义$PS4的值,我们就可以增强"-x"选项的输出信息。例如先执行export PS4='+{$LINENO:${FUNCNAME[0]}} ', 然后再使用"-x"选项来执行脚本,就能在每一条实际执行的命令前面显示其行号以及所属的函数名。

[root@db1 ~]# export PS4='+{$LINENO:${FUNCNAME[0]}}'
[root@db1 ~]# which oracle-rdbms-server-12cR1-preinstall-verify
/usr/bin/oracle-rdbms-server-12cR1-preinstall-verify
[root@db1 ~]# sh -x /usr/bin/oracle-rdbms-server-12cR1-preinstall-verify
我们可以看到,输出的内容如下,输出分别在不同的行

3966 /bin/sed: -e expression #1, char 116: unknown command: `3'

4090 /bin/sed: -e expression #1, char 116: unknown command: `3'


+{570:f_checkuserinfo}/bin/sed -i '/^#[[:space:]]*oracle-rdbms-server-12cR1-preinstall setting for memlock hard limit is maximum of {128GB (x86_64) / 3GB (x86) or 90 % of RAM}/d' /etc/security/limits.d/oracle-rdbms-server-12cR1-preinstall.conf
/bin/sed: -e expression #1, char 116: unknown command: `3'
     
     
+{570:f_checkuserinfo}/bin/sed -i '/^#[[:space:]]*oracle-rdbms-server-12cR1-preinstall setting for memlock soft limit is maximum of {128GB (x86_64) / 3GB (x86) or 90% of RAM}/d' /etc/security/limits.d/oracle-rdbms-server-12cR1-preinstall.conf
/bin/sed: -e expression #1, char 116: unknown command: `3'

从上面我们可以看到出现错误的语句是在570行,函数为f_checkuserinfo,把里面的双引号改成单引号就可以

修改前


568       if [ "x${ARCH}" == "x*" -o "x${ARCH}" == "x${LARCH}" ] ; then
569         if [ ! -z "${COMMENT}" ]; then
570           ${SED} -i /"^#[[:space:]]*$COMMENT"/d ${LIMITSFILE}
571           echo "# $COMMENT" >> ${LIMITSFILE}
572         fi
573       fi
修改后



568       if [ "x${ARCH}" == "x*" -o "x${ARCH}" == "x${LARCH}" ] ; then
569         if [ ! -z "${COMMENT}" ]; then
570           ${SED} -i /'^#[[:space:]]*$COMMENT'/d ${LIMITSFILE}
571           echo "# $COMMENT" >> ${LIMITSFILE}
572         fi
573       fi


小结
我们可以先使用-n选项在语法层面做检查,然后使用-x选项跟踪脚本的执行,使用-x选项之前,最好先定制PS4变量的值来增强-x选项的输出信息,一劳永逸的办法是将这条语句(export PS4='+{$LINENO:${FUNCNAME[0]}}')加到您用户主目录的.bash_profile文件中,这将使你的调试之旅更轻松。也可以利用trap,能够帮你快速缩小排查错误的范围,在脚本中使用set -x及set +x对某些代码块进行重点跟踪。

Reference
http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/

http://www.tldp.org/LDP/abs/


关键词:linux 

相关文章

Install oracle products on docker
How to enable autologin on OEL6/7
Linux的分支有多少,你知道么?
Linux下配置vsftpd
如何配置HITACHI存储多路径软件
Exadata OS用户的密码策略
whats sosreport
Config xming+putty for X11 forwarding
在Linux中如何重置root密码,当你忘记时
在Oracle Enterprise Linux上使用nmon
如何在Windows和Linux上启用Large page
Linux性能优化方面的"神图"
Top