1. 程式人生 > >Linux Shell經典例項解析

Linux Shell經典例項解析

該篇部落格作為對之前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}/dbs/spfile.ora

    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指令碼會將$ORACLE_SID.ora視為一個變數。
    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
}

#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

相關推薦

Linux Shell經典例項解析

該篇部落格作為對之前Linux Shell常用技巧和高階技巧系列部落格的總結,將以Oracle資料庫伺服器啟動指令碼為例,逐行進行解釋和說明,以幫助我們能夠更好的學習和理解Shell指令碼的慣用技巧和強大之處。      Oracle的啟動指令碼從功能上講主要分為兩個部分,

Linux程式設計經典例項

 1、數字輸出 #!/bin/bash echo "Please type your number:" read a for ((i=1;i<=a;i++)) do for((p=1;p<=i;p++)) do echo -n "$p"

資料庫三正規化經典例項解析

資料庫的設計正規化是資料庫設計所需要滿足的規範,滿足這些規範的資料庫是簡潔的、結構明晰的,同時,不會發生插入(insert)、刪除 (delete)和更新(update)操作異常。反之則是亂七八糟,不僅給資料庫的程式設計人員製造麻煩,而且面目可憎,可能儲存了大量不需要的冗餘資訊。      設計正規化是不是很

Linuxshell中exec解析

uil 當前 等等 不同的 exe inux 影響 區別 scrip exec和source都屬於bash內部命令(builtins commands),在bash下輸入man exec或man source可以查看所有的內部命令信息。 bash shell

Linux shell命令解析器(二),命令

環境: Ubuntu14-4   核心 4.4.0-135 vim編輯器 7.4  gcc  4.8.4  1.1 知識點 Shell 的基本概念 檔案相關函式,記憶體相關函式 &nb

一個很牛的 linux shell指令碼編寫的俄羅斯方塊遊戲例項程式碼,非常牛逼

可以將下面程式碼直接儲存為sh檔案。 #!/bin/bash   # Tetris Game # 10.21.2003 xhchen< [email][email protected][/email]>   #APP declaration

Linux shell命令解析器(一),bash終端

環境: Ubuntu14-4   核心 4.4.0-135 vim編輯器 7.4  gcc  4.8.4  1.1 知識點 Shell 的基本概念 程序控制相關的系統呼叫的使用(如 fork,exec函式族) 整理框架: 1.命令直譯器首先是一個死迴圈。 2.

例項解析linux核心I2C體系結構(2)

四、在核心裡寫i2c裝置驅動的兩種方式 在一文介紹了利用/dev/i2c-0在應用層完成對i2c裝置的操作,但很多時候我們還是習慣為i2c裝置在核心層編寫驅動程式。目前核心支援兩種編寫i2c驅動程式的方式。下面分別介紹這兩種方式的實現。這裡分別稱這兩種方式為“Adapter

Linux程式設計之ioremap函式的例項解析

void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 入口: phys_addr:要對映的起始的IO地址; size:要對映的空間的大小; flags:要對映

Linuxshell程式設計例項

1.用while語句建立一個根據輸入的數值求累加和(1+2+3+。。。。+n)的shell程式。   while           若干個命令列1        do              若干個命令列2        done    結構 #! /bin/b

Java經典例項:把字串解析為日期時間

Java版本:1.8開始 import java.time.LocalDate; import java.time.LocalDateTime; /** * Created by Frank */ public class DateParse { public stati

Linux 裝置驅動開發 —— platform裝置驅動應用例項解析

       前面我們已經學習了platform裝置的理論知識Linux 裝置驅動開發 —— platform 裝置驅動 ,下面將通過一個例項來深入我們的學習。 一、platform 驅動的工作過程         platform模型驅動程式設計,需要實現platfor

[轉]linux shell 流程控制(條件if,迴圈【for,while】,選擇【case】語句例項

linux shell有一套自己的流程控制語句,其中包括條件語句(if),迴圈語句(for,while),選擇語句(case)。下面我將通過例子介紹下,各個語句使用方法。 一、shell條件語句(if用法) if語句結構[if/then/elif/else/fi] if 條件測試語句

linux實戰(五)----壓縮備份檔案並刪除原檔案----例項解析

我們在日常的監控中要考慮到磁碟空間的問題,經常會把日誌檔案壓縮備份並刪除原檔案。 我們在基礎(八)中已經學習了壓縮檔案的命令,我們需要做的是把壓縮命令與指令碼結合起來。 這裡以實戰(四)中寫

linux基礎——經典執行緒同步問題解析及程式設計實現

前兩天寫了個簡單的執行緒池,結果在處理執行緒的同步互斥上花了不少時間,覺得有必要把以前學習的知識再過一遍,這次主要複習的是幾個非常經典的同步互斥問題。 一、生產者消費者問題 問題描述: 只有緩衝

Linux多執行緒程式設計例項解析

Linux系統下的多執行緒遵循POSIX執行緒介面,稱為 pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連線時需要使用庫libpthread.a。順便說一下,Linux 下pthread的實現是通過系統呼叫clone()來實現的。clo

linux shell中的命令自動補全(compgen complete)與 命令列引數解析

很多時候,當我們寫一個指令碼時,我們總會提供一些可選的命令選項。當可選項比較多的時候,比如git, 如果能夠提供命令自動補全,無疑是錦上添花的事。而且個人認為,這種方式,比採用將命令做成選擇選單要更好一些。 假設我們現在這樣一個指令碼,指令碼執行命令時bsu,  類似gi

Linux Shell指令碼檔案的判斷、中文符號及其字串入參解析

1、shell指令碼中判斷檔案是否存在 if [ -f  "$var" ] then...... 2、shell指令碼中判斷字串為空  if [ -z "$str"] then...... 3、shell指令碼中判斷字串不為空 if[ "$str"] then..... 4、字串入參的注意事項      

企業Linux系統管理員常用命令大全例項解析(1)

作為一名企業Linux系統管理員,熟悉並熟練運用一些常用的命令是必不可少的,對裝置即檔案的Linux系統來說,Linux系統管理的命令是它正常執行的核心。筆者根據實際工作總結一些常用指令,附上一些

Linux達人養成手冊,帶命令例項解析

今天跟大家分享一些linux離不開的基礎操作命令,也有一些能令你效率增倍的命令,為大家成為達人做墊腳石。 前言:說幾個Linux與Windows最大的幾處不同:Linux中,萬物皆檔案,包括硬體