执行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:执行脚本,进入跟踪方式,显示所执行的每一条命令(将脚本及变量的值输出到屏幕上)
+ 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'
/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/