shell腳本高級進階小總結

  經過一週腳本的折磨,覺得還是有一定的收穫,所以就把一些不容易理解的並且容易忘記的難點做一個總結。shell腳本中主要有兩大模塊,第一就是流程控制的腳本,第二就是函數控制的腳本。

首先,流程控制包括順序執行,選擇執行,循環執行。主要的條件語句就是if。那就來先說說if語句吧!

1.if語句分爲單分支,雙分支和多分支,並且可以嵌套。

2.多分支分爲如下幾種情況

if 判斷條件1; then

條件爲真的分支代碼

elif 判斷條件2; then

條件爲真的分支代碼

elif 判斷條件3; then

條件爲真的分支代碼

else

以上條件都爲假的分支代碼

fi 

逐條件進行判斷,第一次遇爲“真”條件時,執行其分支,而後結束整個if語句

------------下面來舉一些多分支的示例吧

1.判斷/var/目錄下所有文件的類型

for i in /var/* ;do

        if [ -b $i ];then

        echo "$i塊設備文件"

        elif [ -c $i ];then

        echo "$i字符文件"

        elif [ -d $i ];then

        echo "$i目錄文件"

        elif [ -f $i ];then

        echo "$i普通文件"

    elif [ -h $i ];then

        echo "$i鏈接文件"

        elif [ -p $i ];then

        echo "$i管道文件"

        elif [ -s $i ];then

        echo "$i套接字文件"

        else

        echo "$i該文件不存在"

fi

done

其次,再來說說select選擇語句,這個語句一般可以跟case語句合併使用,case和select的語法格式如下。

1.case的語法格式,支持glob風格的通配符:

*: 任意長度任意字符

?: 任意單個字符

[]:指定範圍內的任意單個字符

a|b: a或b

case 變量引用 in

PAT1)

分支1

;;

PAT2)

分支2

;;

...

*)

默認分支

;;

esac 

2.select主要用於創建菜單,按數字順序排列的菜單項將顯示在標準輸入上,並顯示 PS3 提示符,等待用戶輸入用戶輸入菜單列表中的某個數字,執行相應的命令用戶輸入被保存在內置變量 REPLY 中

------------舉例說明,打印菜單

PS3="please choose the menu: "

select menu in  mifan hulatang jiaozi lamian huimian quit

do

case $REPLY in

1|4)

    echo "the price is 20"

    ;;

2)

    echo "the price is 12"

    ;;

3|5)

    echo "the price is 30"

    ;;

6)

    break

    ;;

*)

    echo "no the option"

esac

done                

然後說一下用的比較多的循環語句。循環語句包括for循環和while循環。

  1. for循環語句

    °格式如下:for 變量名 in 列表;do
            循環體
            done
    °執行機制:依次將列表中的元素賦值給“變量名;每次賦值後即執行一次循環體;直到列表中的元素耗盡,循環結束。
    °列表生成方式:
            (1) 直接給出列表
            (2) 整數列表:
            (a) {start..end}
            (b) $(seq [start [step]] end)
            (3) 返回列表的命令
               $(COMMAND)
            (4) 使用glob, 如: *.sh
            (5) 變量引用;
               $@, $*

------------舉例說明,添加10個用戶user1-user10,密碼爲8位隨機字符

for i in {1..10} ; do 

    useradd user$i

    echo "user$i is created..."

     password=`tr -dc 'A-Za-z1-9' < /dev/urandom |head -c8`

     echo $password | passwd --stdin $i &> /dev/null

done

------------舉例說明,/etc/rc.d/rc3.d目錄下分別有多個以K開頭和以S開頭的文件;分別讀取每個文件,以K開頭的輸出爲文件加stop,以S開頭的輸出爲文件名加start,如K34filename stop S66filename start

for i in /etc/rc.d/rc3.d/[SK]* ;do

        if [ $(basename $i |cut -c1) == "k"  ];then

        echo `basename $i` stop

        else 

        echo `basename $i` start

        fi

done 

2.while循環語句

。格式如下:while CONDITION; do

循環體

done

。CONDITION:循環控制條件;進入循環之前,先做一次判斷;每一次循環之後會再次做判斷;條件爲“true”,則執行一次循環;直到條件測試狀態爲“false”終止循環,所以CONDTION一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正。

-----------舉例說明,編寫腳本,利用變量RANDOM生成10個隨機數字,輸出這個10數字,並顯示其中的最大值和最小值

  let i=0,min=max=$RANDOM                                     echo "$min"

while [ $i -lt 9 ];do

    ran=$RANDOM

    echo "$ran"

    if [ $ran -le $max ];then

    let max=ran

    fi

    if [ $ran -le $min ];then

    let min=ran

    fi

    let i+=1

done

echo "最大值是$max,最小值是$min"

-----------舉例說明,後續六個字符串:efbaf275cd、4be9c40b8b、44b2395c46、f8c8873ce0、b902c16c8b、ad865d2f63是通過對隨機數變量RANDOM隨機執行命令:

echo $RANDOM|md5sum|cut –c1-10後的結果,請破解這些字符串對應的RANDOM值

 

num_array=(efbaf275cd 4be9c40b8b 44b2395c46 f8c8873ce0 b902c16c8

b ad865d2f63)

for raw_num in `seq 0 65535`;do

suijijiami=`echo $raw_num | md5sum |cut -c 1-10`

    for num in ${num_array[*]};do

        if [ "$suijijiami" == "$num" ];then                    

            echo "$raw_num*****>$num_array"

         fi

    done

done

解析:這一題其實不難,主要就是要會反向思維,因爲這些字符串都是通過隨機數加密後而成,我們都知道隨機數的一共有65536個數字,也就是說這些加密後的字符串本身沒有加密時肯定都是在0到65535之間,所以我們可以把所有的隨機數按照題目要求的方式隨機加密,然後用for語句匹配我們自己隨機加密的字符串是否跟題目上的字符串相同,如果相同,就取出相同字符串所對應的隨機數,然後就得出了結果。

函數的使用,函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分,函數和shell程序比較相似,區別在於:Shell程序在子Shell中運行而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改。

。函數聲明最好定義在一個單獨的文件中,不要加上shebang機制。然後調用直接調用函數source|. 函數名,因爲函數本身不需要運行,只要在調用的的時候在shell腳本中運行就可以了。

。函數變量默認的是全局變量,所以要定義本地變量隻影響函數內部,避免修改函數外的變量值。

。函數不能用exit退出,因爲函數本身沒有開啓子進程會退出整個腳本

。函數的格式:func1 () { local name=mage;echo func1;echo $name; }

-----------舉例說明

編寫服務腳本/root/bin/testsrv.sh,完成如下要求

(1) 腳本可接受參數:start, stop, restart, status

(2) 如果參數非此四者之一,提示使用格式後報錯退出

(3) 如是start:則創建/var/lock/subsys/SCRIPT_NAME,並顯示“啓動成功”

考慮:如果事先已經啓動過一次,該如何處理?

(4) 如是stop:則刪除/var/lock/subsys/SCRIPT_NAME,並顯示“停止完成”

考慮:如果事先已然停止過了,該如何處理?

(5) 如是restart,則先stop,再start

考慮:如果本來沒有start,如何處理?

(6) 如是status, 則如果/var/lock/subsys/SCRIPT_NAME文件存在,則顯示“SCRIPT_NAMEis running...”

如果/var/lock/subsys/SCRIPT_NAME文件不存在,則顯示“SCRIPT_NAME is stopped...”

其中:SCRIPT_NAME爲當前腳本名

. /etc/init.d/functions

scripts="/var/lock/subsys/`basename $0`" 

start(){

    if [ -e $scripts ];then

        echo "服務已經啓動,不需要再次啓動"

    else

        touch $scripts

        action  "啓動成功" true

    fi

}

stop(){

    if [ -e $scripts ];then

     rm -f $scripts

    action  "停止完成" true

    else

    echo  "服務未啓動,無需停止"

    fi

}

restart(){

    if [ -e $scripts ];then

        stop            

      start

    else

        start

    fi

}

status(){

    if [ -e $scripts ];then

    action "`basename $0` is running......" true

    else

    action "`basename $0` is stopped......" true

    fi

}

case $1 in

    "start")

    start

    ;;

    "stop")

    stop

    ;;

    "restart")

    restart

    ;;                                        

 "status")

    status

    ;;

    *)

    echo  "輸入參數有誤:`basename $0`  is stop|start|restart|status"

    ;;

esac               

解析:本題的要求就是模仿一個服務的各種狀態,當啓動的時候,存在這個服務腳本文件,則顯示已經啓動了不需要再次啓動,如果不存在這個服務腳本就創建這個服務腳本文件,並且提示啓動成功。在停止服務的時候有這個服務腳本就刪除掉這個腳本,並顯示停止成功,如果本身就沒有這個文件,就顯示服務本身就沒有啓動,無需停止。在重啓服務的時候,如果有這個腳本就先停止服務然後重啓,如果不存在這個腳本就直接重啓。狀態就是如果有這個文件就顯示服務開啓,如果沒有就顯示服務停止。

-----------舉例說明

編寫腳本/root/bin/copycmd.sh

(1) 提示用戶輸入一個可執行命令名稱

(2) 獲取此命令所依賴到的所有庫文件列表

(3) 複製命令至某目標目錄(例如/mnt/sysroot)下的對應路徑下;如:/bin/bash ==> /mnt/sysroot/bin/bash

/usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd

(4) 複製此命令依賴到的所有庫文件至目標目錄下的對應路徑下:如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2

(5)每次複製完成一個命令後,不要退出,而是提示用戶鍵入新的要複製的命令,並重復完成上述功能;直到用戶輸入quit退出

. /etc/init.d/functions

while true;

do

read -p "請輸入一個可執行的命令(quit 退出):" command

if [ "$command" == "quit" ] ; then

    exit 1

else

    cmd_path=`which $command`

    mkdir -p /mnt/sysroot$cmd_path

    cp $cmd_path /mnt/sysroot$cmd_path && action " $cmd_path /mnt/sysroot$cmd_path " true

    list=`ldd /bin/ls |grep -o "/lib.* "|tr -d " "`

    [ -e /mnt/sysroot/lib64 -a -e /mnt/sysroot/lib ] || mkdir -p /mnt/sysroot/{lib64,lib}

    for i in $list;do

    cp $i /mnt/sysroot$i && action "$i  /mnt/sysroot$i " true

    done

fi

done              

解析:這題看起來麻煩,其實做的時候並不是很麻煩,就是執行一個腳本,複製命令的路徑到指定的路徑下,然後把庫文件也同時複製過來。要求就是不改變原來的路徑,並且提示用戶退出的時候再退出,不提示就可以一直複製。有一個需要注意的地方就是如果多次複製一個命令的時候不要重複複製,避免出錯。

最後一題還有些地方不是很完善,希望看到的大神幫忙指正。關於腳本的就先寫到這裏了,有時間再補充。。。。。。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章