LinuxShell经典实例解析--Oracle启动脚本(上)该篇博客作为对之前Linux Shell常⽤技巧和⾼级技巧系列博客的总结,将以Oracle数据库服务器启动脚本为例,逐⾏进⾏解释和说明,以帮助我们能够更好的学习和理解Shell脚本的惯⽤技巧和强⼤之处。
Oracle的启动脚本从功能上讲主要分为两个部分,第⼀部分是初始化各种环境变量,以确认当前Oracle服务器的版本,从⽽进⼀步确定启动当前服务器的步骤和具体需要使⽤的各种Oracle⼯具,第⼆部分是基于之前判断的结果,读取当前服务器的各种配置信息,之后再通过Oracle提供的Shell命令完成数据库的启动⼯作。
LOGMSG="logger -puser.alert -s "
#1. 信号捕捉,当脚本捕捉到信号SIGHUP(1)、SIGINT(2)和SIGQUIT(3)时,执⾏exit命令退出脚本。
trap 'exit' 1 2 3
#2. 如果当前Shell环境中指定ORACLE_TRACE变量的值为T,则通过执⾏set -x命令来启动脚本的跟踪功能。
case $ORACLE_TRACE in
T) set -x ;;
esac
SAVE_PATH=/bin:/usr/bin:/etc:${PATH} ; export PATH
SAVE_LLP=$LD_LIBRARY_PATH
#3. $1,即当前脚本的第⼀个参数,通过查看init.d⽬录下调⽤该脚本的Shell脚本oracle,可以获悉该参数的值为$ORACLE_HOME环境变量的值。
ORACLE_HOME_LISTNER=$1
#4. 如果该值不存在,则给出错误提⽰信息,以及该脚本的合法使⽤⽅式。
if [ ! $ORACLE_HOME_LISTNER ] ; then
echo "ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener"
echo "Usage: $0 ORACLE_HOME"
else
LOG=$ORACLE_HOME_LISTNER/listener.log
#5. 导出ORACLE_HOME环境变量的值,由于使⽤了export命令,该变量的值在⼦Shell中将同样有效。
export ORACLE_HOME=$ORACLE_HOME_LISTNER
#6. 判断$ORACLE_HOME_LISTNER/bin/tnslsnr⽂件是否有可执⾏权限,如果为真,则通过该命令启动Oracle监听,需要注意的是,由于在该⾏命令的末尾有⼀个&符号,这表⽰该命令将在后台执⾏。
#7. 在启动监听时,将标准输出以追加的⽅式重定向到$LOG变量指向的⽂件,同时也将标准错误输出也执⾏到该⽂件。
if [ -x $ORACLE_HOME_LISTNER/bin/tnslsnr ] ; then
echo "$0: Starting Oracle Net Listener" >> $LOG 2>&1
$ORACLE_HOME_LISTNER/bin/lsnrctl start >> $LOG 2>&1 &
#8. 通过提取lsnrctl version的返回信息获取当前Oracle服务器的版本,该命令的返回结果为:
#    LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 14-DEC-2011 17:23:12
#
#    Copyright (c) 1991, 2009, Oracle.  All rights reserved.
#
#    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC)))
#    TNSLSNR for Linux: Version 11.2.0.1.0 - Production
#        TNS for Linux: Version 11.2.0.1.0 - Production
#        Unix Domain Socket IPC NT Protocol Adaptor for Linux: Version 11.2.0.1.0 - Production
#        Oracle Bequeath NT Protocol Adapter for Linux: Version 11.2.0.1.0 - Production
#        TCP    /IP NT Protocol Adapter for Linux: Version 11.2.0.1.0 - Production,,
#    The command completed successfully
#9. 在通过grep命令对以上结果进⾏过滤,只输出包含"LSNRCTL for"的⾏,其结果为:
#    LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 14-DEC-2011 17:25:21
#10.通过cut命令对以上结果进⾏拆分,分隔符为-d选项指定的空格字符,-f5表⽰将输出拆分后的第五个字段,其结果为:
#    11.2.0.1.0
#11.通过cut命令对以上结果进⾏⼆次拆分,但是这次的分隔符改为点(.),本次获取的字段为第⼀个字段,即11。
VER10LIST=`$ORACLE_HOME_LISTNER/bin/lsnrctl version | grep "LSNRCTL for " | cut -d' ' -f5 | cut -d'.' -f1`
export VER10LIST
else
echo "Failed to auto-start Oracle Net Listener using $ORACLE_HOME_LISTNER/bin/tnslsnr"
fi
fi
ORATAB=/etc/oratab
#12.我想此处代码的本意应为判断/etc/oratab⽂件是否以⽂件的形式存在,然⽽下⾯的写法将会使if判断永远为真,因此应改为if [ ! -f $ORATAB ]; then。-f⽤于判断其后的变量是否是为普通⽂件。如果该⽂件不存在,脚本将直接退出,退出值为1,表⽰失败。需要说明的是,在Linux中,通⽤的规则是返回0表⽰执⾏成功。
if [ ! $ORATAB ] ; then
echo "$ORATAB not found"
exit 1;
fi
#13. checkversionmismatch是该脚本的⾃定义函数,⽤于判断客户端⼯具sqlplus和Oracle服务器之间的版本是否匹配。checkversionmismatch() {
if [ $VER10LIST ] ; then
#14. 通过sqlplus -V获取sqlplus的版本,再该通过grep命令过滤,仅输出包含Release的⾏,其结果为:
#    SQL*Plus: Release 11.2.0.1.0 Production
#15. 基于以上结果,再通过两次cut命令的拆分,最后输出:11。这⾥cut的作⽤已经在上⾯的注释中给出。
VER10INST=`sqlplus -V | grep "Release " | cut -d' ' -f3 | cut -d'.' -f1`
#16. 如果服务器的版本($VER10LIST)⼩于sqlplus的版本(VER10INST),将输出不匹配的提⽰信息。这⾥-lt⽤于⽐较数值型变量,表⽰A ⼩于 B。
if [ $VER10LIST -lt $VER10INST ] ; then
$LOGMSG "Listener version $VER10LIST NOT supported with Database version $VER10INST"
$LOGMSG "Restart Oracle Net Listener using an alternate ORACLE_HOME_LISTNER:"
$LOGMSG "lsnrctl start"
fi
fi
}
startinst() {
export ORACLE_SID
#17. 将oracle的bin⽬录放置到PATH环境变量中,已便于之后的直接调⽤。
PATH=$ORACLE_HOME/bin:${SAVE_PATH} ; export PATH
#18. LD_LIBRARY_PATH指出so⽂件所在的路径,这⾥将oracle所依赖的lib的路径赋值给该变量,以便oracle执⾏程序在启动时可以到他们。
LD_LIBRARY_PATH=${ORACLE_HOME}/lib:${SAVE_LLP} ; export LD_LIBRARY_PATH
#19. 下⾯的变量是oracle启动时所需要的服务器实例初始化⽂件。
PFILE=${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora
SPFILE=${ORACLE_HOME}/dbs/spfile${ORACLE_SID}.ora
SPFILE1=${ORACLE_HOME}/a
echo ""
echo "$0: Starting up database \"$ORACLE_SID\""
date
echo ""
checkversionmismatch
#20. 下⾯的代码逻辑⽤于区分当前服务器的版本是否为V6或V7,因为后⾯的启动逻辑需要为这两个版本做特殊处理。
#21. ⾸先判断$ORACLE_HOME/bin/sqldba是否以普通⽂件的形式存在,如果存在,将通过sqldba命令获取版本信息。
VERSION=undef
if [ -f $ORACLE_HOME/bin/sqldba ] ; then
SQLDBA=sqldba
VERSION=`$ORACLE_HOME/bin/sqldba command=exit | awk '
/SQL\*DBA: (Release|Version)/ {split($3, V, ".") ;
print V[1]}'`
#22. 如果版本为6,则什么也不⽤做,否则将VERSION变量的值统⼀为internal。
case $VERSION in
"6") ;;
*) VERSION="internal"
esac
else
#23. 再次判断$ORACLE_HOME/bin/svrmgrl是否以普通⽂件的形式存在,如果存在,SQLDBA的命令将为svrmgrl,版本为internal,否则SQLDBA命令将指向sqlplus。需要说明的是,不管是这⾥的svrmgrl还是上⾯的sqldba,都是为了向以前版本的兼容,才⽤SQLDBA来动态的表⽰他们,事实上,在我们后
来的版本中,基本都是使⽤sqlplus。
if [ -f $ORACLE_HOME/bin/svrmgrl ] ; then
SQLDBA=svrmgrl
VERSION="internal"
else
SQLDBA="sqlplus /nolog"
fi
fi
#24. 变量STATUS为1时表⽰正常值,其它值均表⽰oracle的进程已经拉起。
#25. 先是判断$ORACLE_HOME/dbs/sgadef${ORACLE_SID}.dbf和$ORACLE_HOME/dbs/sgadef${ORACLE_SID}.ora这两个⽂件是否已经存在。其中${ORACLE_SID}表⽰变量,shell在执⾏时会使⽤该变量的实际值予以替换,这⾥之所有⽤花括号括起${ORACLE_SID},
⽽不是直接使⽤$ORACLE_SID,是因为如果这样使⽤的话,shell脚本会将$a视为⼀个变量。
STATUS=1
if [ -f $ORACLE_HOME/dbs/sgadef${ORACLE_SID}.dbf ] ; then
STATUS="-1"
fi
if [ -f $ORACLE_HOME/dbs/sgadef${ORACLE_SID}.ora ] ; then
STATUS="-1"
fi
#26. pmon是oracle的进程监控进程,是oracle服务器的核⼼进程之⼀。这⾥通过ps命令输出当前linux服务器所有进程的列表,再通过grep命令进⾏过滤,其中-w选择表⽰全词匹配,最后再通过⼀个grep命令过滤掉上⼀个grep命令,这⾥的-v表⽰取反,即不包含grep的⾏。    pmon=`ps -ef | grep -w "ora_pmon_$ORACLE_SID"  | grep -v grep`
if [ "$pmon" != "" ] ; then
STATUS="-1"
$LOGMSG "Warning: ${INST} \"${ORACLE_SID}\" already started."
fi
#27. 这⾥是判断数值型变量$STATUS是否为-1,即进程已经启动。
if [ $STATUS -eq -1 ] ; then
$LOGMSG "Warning: ${INST} \"${ORACLE_SID}\" possibly left running when system went down (system crash?)."
$LOGMSG "Action: Notify Database Administrator."
#28. 既然oracle服务器实例已经启动,这⾥就需要根据oracle的版本,⽤不同的⼯具和关闭语法shutdown已经启动的实例。
case $VERSION in
"6")  sqldba "command=shutdown abort" ;;
"internal")  $SQLDBA $args <<EOF
connect internal
shutdown abort
EOF
;;
*)  $SQLDBA $args <<EOF
connect / as sysdba
shutdown abort
quit
EOF
;
;
esac
#29. $?是shell脚本的内置变量,⽤于判断上⾯关闭oracle服务器实例的操作是否成功,0表⽰成功,其他值均表⽰失败。
if [ $? -eq 0 ] ; then
STATUS=1
else
$LOGMSG "Error: ${INST} \"${ORACLE_SID}\" NOT started."
fi
fi
if [ $STATUS -eq 1 ] ; then
#30. 判断$SPFILE、$SPFILE1或$PFILE是否存在,-e表⽰其后⾯的变量表⽰的⽂件是否存在,-o表⽰这⼏个条件时间的或关系,即C 语⾔中的||。
#31. 根本oracle的版本,⽤不同的oracle⼯具启动oracle服务器实例,其中不同的⼯具所使⽤的语法也不同,这⾥我们主要需要关注的是sqlplus。
#32. 在通过oracle⼯具启动服务器时,这⾥使⽤了shell中的HERE DOCUMENT,这样可以将⼀批命令⼀次性传递给sqlplus这样的oracle命令。
if [ -e $SPFILE -o -e $SPFILE1 -o -e $PFILE ] ; then
case $VERSION in
"6")  sqldba command=startup ;;
"internal") $SQLDBA <<EOF
connect internal
startup
EOF
;;
*) $SQLDBA <<EOF
connect / as sysdba
startup
quit
EOF
;;
esac
#33. 通过判断以上命令的返回值,来判断是否启动成功。
if [ $? -eq 0 ] ; then
echo ""
echo "$0: ${INST} \"${ORACLE_SID}\" warm started."
else
$LOGMSG ""
$LOGMSG "Error: ${INST} \"${ORACLE_SID}\" NOT started."
fi
else
$LOGMSG ""
$LOGMSG "No init file found for ${INST} \"${ORACLE_SID}\"."
$LOGMSG "Error: ${INST} \"${ORACLE_SID}\" NOT started."
fi
fi
shell脚本返回执行结果}
#34. ⽤于启动oracle的AMS实例的函数。
startasminst() {
export ORACLE_SID
#34. $LINE的值在后⾯的调⽤中会给出,该值源⾃oratab⽂件的输出,其内容为:MyOrcl:/opt/oracle/product/OraHome:Y
#35. 这⾥使⽤awk命令提取第⼆个域字段,其中冒号(:)为各个域之间的分隔符,第⼆个变量($2)为当前实例的oracle主⽬录。
ORACLE_HOME=`echo $LINE | awk -F: '{print $2}' -`
export ORACLE_HOME
#36. 判断$ORACLE_HOME/bin/crsctl是否有执⾏权限。
if [ ! -x $ORACLE_HOME/bin/crsctl ]; then
$LOGMSG "$ORACLE_HOME/bin/crsctl not found when attempting to start"
$LOGMSG "  ASM instance $ORACLE_SID."
else
#37. 反复执⾏$ORACLE_HOME/bin/crsctl命令,直到其执⾏成功,或在执⾏15次失败后退出脚本。
COUNT=0
$ORACLE_HOME/bin/crsctl check css
RC=$?
#38. 判断crsctl命令是否执⾏成功,如果不等于表⽰执⾏失败,则继续执⾏。
while [ "$RC" != "0" ]; do
#39. 通过expr命令,将COUNT的变量值加⼀,这⾥也可以使⽤let命令,如((COUNT=COUNT+1))。
COUNT=`expr $COUNT + 1`
if [ $COUNT = 15 ] ; then
# 15 tries with 20 sec interval => 5 minutes timeout
$LOGMSG "Timed out waiting to start ASM instance $ORACLE_SID"
$LOGMSG "  CSS service is NOT available."
exit $COUNT
fi
$LOGMSG "Waiting for Oracle CSS service to be available before starting "
$LOGMSG " ASM instance $ORACLE_SID. Wait $COUNT."
#40. 每次执⾏之间都休眠20秒。
sleep 20
$ORACLE_HOME/bin/crsctl check css
RC=$?
done
fi
#41. asm在启动成功后,调⽤startinst函数启动该实例。
startinst
}
在这篇博客中,只是给出了oracle启动脚本中⾃定义函数的解释,在下⼀篇博客中将进⼊该脚本的主体部分,其中主体部分的代码将依赖于本篇博客中给出的函数。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。