找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 7104|回复: 0

Linux Shell经典实例解析--Oracle启动脚本(上)

[复制链接]
发表于 2012-4-23 13:55:47 | 显示全部楼层 |阅读模式
  1.       该篇博客作为对之前Linux Shell常用技巧和高级技巧系列博客的总结,将以Oracle数据库服务器启动脚本为例,逐行进行解释和说明,以帮助我们能够更好的学习和理解Shell脚本的惯用技巧和强大之处。
  2.       Oracle的启动脚本从功能上讲主要分为两个部分,第一部分是初始化各种环境变量,以确认当前Oracle服务器的版本,从而进一步确定启动当前服务器的步骤和具体需要使用的各种Oracle工具,第二部分是基于之前判断的结果,读取当前服务器的各种配置信息,之后再通过Oracle提供的Shell命令完成数据库的启动工作。
  3. LOGMSG="logger -puser.alert -s "
  4. #1. 信号捕捉,当脚本捕捉到信号SIGHUP(1)、SIGINT(2)和SIGQUIT(3)时,执行exit命令退出脚本。
  5. trap 'exit' 1 2 3
  6. #2. 如果当前Shell环境中指定ORACLE_TRACE变量的值为T,则通过执行set -x命令来启动脚本的跟踪功能。
  7. case $ORACLE_TRACE in
  8.     T) set -x ;;
  9. esac
  10. SAVE_PATH=/bin:/usr/bin:/etc:${PATH} ; export PATH
  11. SAVE_LLP=$LD_LIBRARY_PATH
  12. #3. $1,即当前脚本的第一个参数,通过查看init.d目录下调用该脚本的Shell脚本oracle,可以获悉该参数的值为$ORACLE_HOME环境变量的值。
  13. ORACLE_HOME_LISTNER=$1
  14. #4. 如果该值不存在,则给出错误提示信息,以及该脚本的合法使用方式。
  15. if [ ! $ORACLE_HOME_LISTNER ] ; then
  16.     echo "ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener"
  17.     echo "Usage: $0 ORACLE_HOME"
  18. else
  19.     LOG=$ORACLE_HOME_LISTNER/listener.log
  20.     #5. 导出ORACLE_HOME环境变量的值,由于使用了export命令,该变量的值在子Shell中将同样有效。
  21.     export ORACLE_HOME=$ORACLE_HOME_LISTNER
  22.     #6. 判断$ORACLE_HOME_LISTNER/bin/tnslsnr文件是否有可执行权限,如果为真,则通过该命令启动Oracle监听,需要注意的是,由于在该行命令的末尾有一个&符号,这表示该命令将在后台执行。
  23.     #7. 在启动监听时,将标准输出以追加的方式重定向到$LOG变量指向的文件,同时也将标准错误输出也执行到该文件。
  24.     if [ -x $ORACLE_HOME_LISTNER/bin/tnslsnr ] ; then
  25.         echo "$0: Starting Oracle Net Listener" >> $LOG 2>&1
  26.         $ORACLE_HOME_LISTNER/bin/lsnrctl start >> $LOG 2>&1 &
  27.         #8. 通过提取lsnrctl version的返回信息获取当前Oracle服务器的版本,该命令的返回结果为:
  28.         #    LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 14-DEC-2011 17:23:12
  29.         #
  30.         #    Copyright (c) 1991, 2009, Oracle.  All rights reserved.
  31.         #
  32.         #    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC)))
  33.         #    TNSLSNR for Linux: Version 11.2.0.1.0 - Production
  34.         #        TNS for Linux: Version 11.2.0.1.0 - Production
  35.         #        Unix Domain Socket IPC NT Protocol Adaptor for Linux: Version 11.2.0.1.0 - Production
  36.         #        Oracle Bequeath NT Protocol Adapter for Linux: Version 11.2.0.1.0 - Production
  37.         #        TCP    /IP NT Protocol Adapter for Linux: Version 11.2.0.1.0 - Production,,
  38.         #    The command completed successfully
  39.         #9. 在通过grep命令对以上结果进行过滤,只输出包含"LSNRCTL for"的行,其结果为:
  40.         #    LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 14-DEC-2011 17:25:21
  41.         #10.通过cut命令对以上结果进行拆分,分隔符为-d选项指定的空格字符,-f5表示将输出拆分后的第五个字段,其结果为:
  42.         #    11.2.0.1.0
  43.         #11.通过cut命令对以上结果进行二次拆分,但是这次的分隔符改为点(.),本次获取的字段为第一个字段,即11。   
  44.         VER10LIST=`$ORACLE_HOME_LISTNER/bin/lsnrctl version | grep "LSNRCTL for " | cut -d' ' -f5 | cut -d'.' -f1`
  45.         export VER10LIST
  46.     else
  47.         echo "Failed to auto-start Oracle Net Listener using $ORACLE_HOME_LISTNER/bin/tnslsnr"
  48.     fi
  49. fi
  50. ORATAB=/etc/oratab
  51. #12.我想此处代码的本意应为判断/etc/oratab文件是否以文件的形式存在,然而下面的写法将会使if判断永远为真,因此应改为if [ ! -f $ORATAB ]; then。-f用于判断其后的变量是否是为普通文件。如果该文件不存在,脚本将直接退出,退出值为1,表示失败。需要说明的是,在Linux中,通用的规则是返回0表示执行成功。
  52. if [ ! $ORATAB ] ; then
  53.     echo "$ORATAB not found"
  54.     exit 1;
  55. fi
  56. #13. checkversionmismatch是该脚本的自定义函数,用于判断客户端工具sqlplus和Oracle服务器之间的版本是否匹配。
  57. checkversionmismatch() {
  58.     if [ $VER10LIST ] ; then
  59.         #14. 通过sqlplus -V获取sqlplus的版本,再该通过grep命令过滤,仅输出包含Release的行,其结果为:
  60.         #    SQL*Plus: Release 11.2.0.1.0 Production
  61.         #15. 基于以上结果,再通过两次cut命令的拆分,最后输出:11。这里cut的作用已经在上面的注释中给出。
  62.         VER10INST=`sqlplus -V | grep "Release " | cut -d' ' -f3 | cut -d'.' -f1`
  63.         #16. 如果服务器的版本($VER10LIST)小于sqlplus的版本(VER10INST),将输出不匹配的提示信息。这里-lt用于比较数值型变量,表示A 小于 B。
  64.         if [ $VER10LIST -lt $VER10INST ] ; then
  65.             $LOGMSG "Listener version $VER10LIST NOT supported with Database version $VER10INST"
  66.             $LOGMSG "Restart Oracle Net Listener using an alternate ORACLE_HOME_LISTNER:"
  67.             $LOGMSG "lsnrctl start"
  68.         fi
  69.     fi
  70. }
  71. startinst() {
  72.     export ORACLE_SID
  73.     #17. 将oracle的bin目录放置到PATH环境变量中,已便于之后的直接调用。
  74.     PATH=$ORACLE_HOME/bin:${SAVE_PATH} ; export PATH
  75.     #18. LD_LIBRARY_PATH指出so文件所在的路径,这里将oracle所依赖的lib的路径赋值给该变量,以便oracle执行程序在启动时可以找到他们。
  76.     LD_LIBRARY_PATH=${ORACLE_HOME}/lib:${SAVE_LLP} ; export LD_LIBRARY_PATH
  77.     #19. 下面的变量是oracle启动时所需要的服务器实例初始化文件。
  78.     PFILE=${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora
  79.     SPFILE=${ORACLE_HOME}/dbs/spfile${ORACLE_SID}.ora
  80.     SPFILE1=${ORACLE_HOME}/dbs/spfile.ora
  81.    
  82.     echo ""
  83.     echo "$0: Starting up database "$ORACLE_SID""
  84.     date
  85.     echo ""
  86.     checkversionmismatch
  87.    
  88.     #20. 下面的代码逻辑用于区分当前服务器的版本是否为V6或V7,因为后面的启动逻辑需要为这两个版本做特殊处理。
  89.     #21. 首先判断$ORACLE_HOME/bin/sqldba是否以普通文件的形式存在,如果存在,将通过sqldba命令获取版本信息。
  90.     VERSION=undef
  91.     if [ -f $ORACLE_HOME/bin/sqldba ] ; then
  92.         SQLDBA=sqldba
  93.         VERSION=`$ORACLE_HOME/bin/sqldba command=exit | awk '
  94.             /SQL\*DBA: (Release|Version)/ {split($3, V, ".") ;
  95.             print V[1]}'`
  96.         #22. 如果版本为6,则什么也不用做,否则将VERSION变量的值统一为internal。
  97.         case $VERSION in
  98.             "6") ;;
  99.             *) VERSION="internal"
  100.         esac
  101.     else
  102.         #23. 再次判断$ORACLE_HOME/bin/svrmgrl是否以普通文件的形式存在,如果存在,SQLDBA的命令将为svrmgrl,版本为internal,否则SQLDBA命令将指向sqlplus。需要说明的是,不管是这里的svrmgrl还是上面的sqldba,都是为了向以前版本的兼容,才用SQLDBA来动态的表示他们,事实上,在我们后来的版本中,基本都是使用sqlplus。
  103.         if [ -f $ORACLE_HOME/bin/svrmgrl ] ; then
  104.             SQLDBA=svrmgrl
  105.             VERSION="internal"
  106.         else
  107.             SQLDBA="sqlplus /nolog"
  108.         fi
  109.     fi   
  110.     #24. 变量STATUS为1时表示正常值,其它值均表示oracle的进程已经拉起。
  111.     #25. 先是判断$ORACLE_HOME/dbs/sgadef${ORACLE_SID}.dbf和$ORACLE_HOME/dbs/sgadef${ORACLE_SID}.ora这两个文件是否已经存在。其中${ORACLE_SID}表示变量,shell在执行时会使用该变量的实际值予以替换,这里之所有用花括号括起${ORACLE_SID},而不是直接使用$ORACLE_SID,是因为如果这样使用的话,shell脚本会将$ORACLE_SID.ora视为一个变量。
  112.     STATUS=1
  113.     if [ -f $ORACLE_HOME/dbs/sgadef${ORACLE_SID}.dbf ] ; then
  114.         STATUS="-1"
  115.     fi
  116.     if [ -f $ORACLE_HOME/dbs/sgadef${ORACLE_SID}.ora ] ; then
  117.         STATUS="-1"
  118.     fi
  119.     #26. pmon是oracle的进程监控进程,是oracle服务器的核心进程之一。这里通过ps命令输出当前linux服务器所有进程的列表,再通过grep命令进行过滤,其中-w选择表示全词匹配,最后再通过一个grep命令过滤掉上一个grep命令,这里的-v表示取反,即不包含grep的行。
  120.     pmon=`ps -ef | grep -w "ora_pmon_$ORACLE_SID"  | grep -v grep`
  121.     if [ "$pmon" != "" ] ; then
  122.         STATUS="-1"
  123.         $LOGMSG "Warning: ${INST} "${ORACLE_SID}" already started."
  124.     fi
  125.     #27. 这里是判断数值型变量$STATUS是否为-1,即进程已经启动。
  126.     if [ $STATUS -eq -1 ] ; then
  127.         $LOGMSG "Warning: ${INST} "${ORACLE_SID}" possibly left running when system went down (system crash?)."
  128.         $LOGMSG "Action: Notify Database Administrator."
  129.         #28. 既然oracle服务器实例已经启动,这里就需要根据oracle的版本,用不同的工具和关闭语法shutdown已经启动的实例。
  130.         case $VERSION in
  131.             "6")  sqldba "command=shutdown abort" ;;
  132.             "internal")  $SQLDBA $args <<EOF
  133.         connect internal
  134.         shutdown abort
  135.         EOF
  136.             ;;
  137.             *)  $SQLDBA $args <<EOF
  138.         connect / as sysdba
  139.         shutdown abort
  140.         quit
  141.         EOF
  142.             ;;
  143.         esac
  144.         #29. $?是shell脚本的内置变量,用于判断上面关闭oracle服务器实例的操作是否成功,0表示成功,其他值均表示失败。
  145.         if [ $? -eq 0 ] ; then
  146.             STATUS=1
  147.         else
  148.             $LOGMSG "Error: ${INST} "${ORACLE_SID}" NOT started."
  149.         fi
  150.     fi
  151.     if [ $STATUS -eq 1 ] ; then
  152.         #30. 判断$SPFILE、$SPFILE1或$PFILE是否存在,-e表示其后面的变量表示的文件是否存在,-o表示这几个条件时间的或关系,即C语言中的||。
  153.         #31. 根本oracle的版本,用不同的oracle工具启动oracle服务器实例,其中不同的工具所使用的语法也不同,这里我们主要需要关注的是sqlplus。
  154.         #32. 在通过oracle工具启动服务器时,这里使用了shell中的HERE DOCUMENT,这样可以将一批命令一次性传递给sqlplus这样的oracle命令。
  155.         if [ -e $SPFILE -o -e $SPFILE1 -o -e $PFILE ] ; then
  156.             case $VERSION in
  157.                 "6")  sqldba command=startup ;;
  158.                 "internal") $SQLDBA <<EOF
  159.             connect internal
  160.             startup
  161.             EOF
  162.                 ;;
  163.                 *) $SQLDBA <<EOF
  164.             connect / as sysdba
  165.             startup
  166.             quit
  167.             EOF
  168.                 ;;
  169.             esac
  170.             #33. 通过判断以上命令的返回值,来判断是否启动成功。
  171.             if [ $? -eq 0 ] ; then
  172.                 echo ""
  173.                 echo "$0: ${INST} "${ORACLE_SID}" warm started."
  174.             else
  175.                 $LOGMSG ""
  176.                 $LOGMSG "Error: ${INST} "${ORACLE_SID}" NOT started."
  177.             fi
  178.         else
  179.             $LOGMSG ""
  180.             $LOGMSG "No init file found for ${INST} "${ORACLE_SID}"."
  181.             $LOGMSG "Error: ${INST} "${ORACLE_SID}" NOT started."
  182.         fi
  183.     fi
  184. }
  185. #34. 用于启动oracle的AMS实例的函数。
  186. startasminst() {
  187.     export ORACLE_SID
  188.     #34. $LINE的值在后面的调用中会给出,该值源自oratab文件的输出,其内容为:MyOrcl:/opt/oracle/product/OraHome:Y
  189.     #35. 这里使用awk命令提取第二个域字段,其中冒号(:)为各个域之间的分隔符,第二个变量($2)为当前实例的oracle主目录。
  190.     ORACLE_HOME=`echo $LINE | awk -F: '{print $2}' -`
  191.     export ORACLE_HOME
  192.    
  193.     #36. 判断$ORACLE_HOME/bin/crsctl是否有执行权限。
  194.     if [ ! -x $ORACLE_HOME/bin/crsctl ]; then
  195.         $LOGMSG "$ORACLE_HOME/bin/crsctl not found when attempting to start"
  196.         $LOGMSG "  ASM instance $ORACLE_SID."
  197.     else
  198.         #37. 反复执行$ORACLE_HOME/bin/crsctl命令,直到其执行成功,或在执行15次失败后退出脚本。
  199.         COUNT=0
  200.         $ORACLE_HOME/bin/crsctl check css
  201.         RC=$?
  202.         #38. 判断crsctl命令是否执行成功,如果不等于表示执行失败,则继续执行。
  203.         while [ "$RC" != "0" ]; do
  204.             #39. 通过expr命令,将COUNT的变量值加一,这里也可以使用let命令,如((COUNT=COUNT+1))。
  205.             COUNT=`expr $COUNT + 1`
  206.             if [ $COUNT = 15 ] ; then
  207.                 # 15 tries with 20 sec interval => 5 minutes timeout
  208.                 $LOGMSG "Timed out waiting to start ASM instance $ORACLE_SID"
  209.                 $LOGMSG "  CSS service is NOT available."
  210.                 exit $COUNT
  211.             fi
  212.             $LOGMSG "Waiting for Oracle CSS service to be available before starting "
  213.             $LOGMSG " ASM instance $ORACLE_SID. Wait $COUNT."
  214.             #40. 每次执行之间都休眠20秒。
  215.             sleep 20
  216.             $ORACLE_HOME/bin/crsctl check css
  217.             RC=$?
  218.         done
  219.     fi
  220.     #41. asm在启动成功后,调用startinst函数启动该实例。
  221.     startinst
  222. }
  223.      在这篇博客中,只是给出了oracle启动脚本中自定义函数的解释,在下一篇博客中将进入该脚本的主体部分,其中主体部分的代码将依赖于本篇博客中给出的函数。
复制代码


目录:
Linux Shell高级技巧(一)
http://www.acejoy.com/thread-4326-1-1.html
一、将输入信息转换为大写字符后再进行条件判断
二、为调试信息设置输出级别
三、判断参数是否为数字
四、判断整数变量的奇偶性
五、将Shell命令赋值给指定变量,以保证脚本的移植性
六、获取当前时间距纪元时间(1970年1月1日)所经过的天数
Linux Shell高级技巧(二)
http://www.acejoy.com/thread-4327-1-1.html
七、非直接引用变量
八、在循环中使用管道的技巧
九、自链接脚本
十、Here文档的使用技巧
十一、获取进程的运行时长(单位: 分钟)
十二、模拟简单的top命令
Linux Shell高级技巧(三)
http://www.acejoy.com/thread-4328-1-1.html
十三、格式化输出指定用户的当前运行进程
十四、用脚本完成which命令的基本功能
十五、验证输入信息是否合法
十六、整数验证
十七、判断指定的年份是否为闰年
十八、将单列显示转换为多列显示
Linux Shell高级技巧(四)
http://www.acejoy.com/thread-4329-1-1.html
十九、将文件的输出格式化为指定的宽度
二十、监控指定目录下磁盘使用空间过大的用户
二十一、编写一个更具可读性的df命令输出脚本
二十二、编写一个用于添加新用户的脚本
二十三、kill指定用户或指定终端的用户进程
二十四、判断用户输入(是/否)的便捷方法
Linux Shell高级技巧(五)
http://www.acejoy.com/thread-4330-1-1.html
二十五、通过FTP下载指定的文件
二十六、文件锁定
二十七、用小文件覆盖整个磁盘
二十八、统计当前系统中不同运行状态的进程数量
二十九、浮点数验证
三十、统计英文文章中每个单词出现的频率

Linux Shell经典实例解析--Oracle启动脚本(上)
http://www.acejoy.com/thread-4331-1-1.html

Linux Shell经典实例解析--Oracle启动脚本(下)
http://www.acejoy.com/thread-4332-1-1.html


您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-7 12:38 , Processed in 0.015745 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表