shell高級編程

  • 條件選擇if語句

        選擇執行: 注意:if語句可嵌套 
        單分支
        if 判斷條件;then
        條件爲真的分支代碼
        fi
    
        雙分支
        if 判斷條件; then
        條件爲真的分支代碼
        else條件爲假的分支代碼
        fi
    
        多分支
        if 判斷條件1; then
        條件爲真的分支代碼
        elif 判斷條件2; then
        條件爲真的分支代碼
        elif 判斷條件3; then
        條件爲真的分支代碼
        else以上條件都爲假的分支代碼
        fi
        逐個條件進行判斷,第一次遇爲“真”條件時,執行其分支,而後結束整個if語
        句

If示例
根據命令的退出狀態來執行命令

if ping -c1 -W2 station1 &> /dev/null; then
echo 'Station1 is UP'
elif grep "station1" ~/maintenance.txt &> /dev/null
then
echo 'Station1 is undergoing maintenance‘
else echo 'Station1 is unexpectedly DOWN!' exit 1
fi

        條件判斷:case語句

        case 變量引用 in
        PAT1)
                     分支1
                        ;;
        PAT2)
                        分支2
        ;;
                 ...*)
        默認分支
        ;;
        esac

        case支持glob風格的通配符:
        *: 任意長度任意字符
        ?: 任意單個字符
        []:指定範圍內的任意單個字符
        a|b: a或b
  • for循環

         for 變量名 in 列表;do
        循環體
        done
        執行機制:
        依次將列表中的元素賦值給“變量名”; 每次賦值後即執行一次循環體; 直到列表中的元素
        耗盡,循環結束

列表生成方式:

(1) 直接給出列表
(2) 整數列表:
(a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令$(COMMAND)
(4) 使用glob,如:.sh
(5) 變量引用;$@, $

  • while循環

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

        循環控制語句continue
        用於循環體中
        continue [N]:提前結束第N層的本輪循環,而直接進入下一輪判斷;最內層爲第1層
        while CONDTIITON1; do
        CMD1
        ...
        if CONDITION2; then
        continue
        fi
        CMDn
        ...
        done
    
        循環控制語句break
        用於循環體中
        break [N]:提前結束第N層循環,最內層爲第1層
        while CONDTIITON1; do
        CMD1...if CONDITION2; then
        breakfiCMDn...done

示例:doit.sh

        #!/bin/bash
         Name: doit.sh
        Purpose: shift through command line arguments
        Usage: doit.sh [args]
        while [ $# -gt 0 ] # or (( $# > 0 ))
        doecho $*
        shiftdone

示例:shift.sh

        #!/bin/bash
        #step through all the positional parameters
        until [ -z "$1" ]
        doecho "$1"
        shiftdoneecho

        創建無限循環
        while true; do
        循環體
        done 

        until false; do
        循環體
        Done

    特殊用法
    while循環的特殊用法(遍歷文件的每一行):
    while read line; do
    循環體
    done < /PATH/FROM/SOMEFILE
    依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將行賦值給變量line

練習
掃描/etc/passwd文件每一行,如發現GECOS字段爲空,則填充用戶名和單位電話爲62985600,並提示該
用戶的GECOS信息修改成功。

         #!/bin/bash
         while read line ;do
                         gecos=$(echo $line |cut -d: -f5)
                         if [ -z "$gecos" ];then
                                         UserName=$(echo $line |cut -d: -f1)
                                         usermod -c "$UserName 62985600" $UserName
                                         echo "$UserName's gecos changed"
                         fi
         done < /etc/passwd

編寫個腳本,會對系統中已存在的用戶進行身份判斷,若爲centos7,則uid大於1000的用戶將判斷爲comm user,反之判定爲sys user, 若爲centos6,則uid大於500的用戶判斷爲comm user,反之sys user.輸出格式如下
root: sys user
……
liubei: comm user

         #!/bin/bash
         release=$(cat /etc/centos-release| sed -r 's/.* ([0-9]+)..*/\1/')
         while read line; do
                 uid=$(echo $line | cut -d: -f3)
                 name=$(echo $line | cut -d: -f1)
                 if [ $release = 6 -a $uid -lt 500 ] || [ $release = 7 -a $uid -lt 1000 ]; then
                         echo "$name: sys user"
                 else
                         echo "$name: comm user"
                 fi    
         done < /etc/passwd

找出分區利用率大於10%的分區,顯示結果如下:
/dev/sda1 will be full : 33%
/dev/sda2 will be full : 99%

         #!/bin/bash
         df |grep /dev/sd |while read line;do
                         used=$(echo $line |tr -s " " % |cut -d% -f5)
                         name=$(echo $line |cut -d" " -f1)
                         if (( $used > 10 ));then
                                         echo "$name will be full:$used%"
                        fi
         done
  • 特殊用法

        雙小括號方法,即((…))格式,也可以用於算術運算
        雙小括號方法也可以使bash Shell實現C語言風格的變量操作
        I=10((I++))
        for循環的特殊格式:
        for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式))
        do循環體
        done控制變量初始化:僅在運行到循環代碼段時執行一次
        控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而後再做條件判斷
    
        for ((i=1;i<=100;i++));do
                        let sum+=i
        done
        echo sum=$sum
    
        select循環與菜單
        select variable in list
        do
        循環體命令
        done
        select 循環主要用於創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,並顯示 PS3 提示符,等待用戶輸入
        用戶輸入菜單列表中的某個數字,執行相應的命令
        用戶輸入被保存在內置變量 REPLY 中[][][]
    
        select 是個無限循環,因此要記住用 break 命令退出循環,或用 exit 命令終止腳本。也可以按
        ctrl+c 退出循環
        select 經常和 case 聯合使用
        與 for 循環類似,可以省略 in list,此時使用位置參量

練習:爲某餐館用Shell製作一個點菜系統。
執行腳本,會列出主菜單,如下
請問吃什麼?
1) 飯
2) 面
3)餃子
4)不吃

等待用戶選擇
如選擇1,則再問,選擇完後報價
1) 炒飯
2)蓋飯
3)木桶飯
如選擇2,則再問
1)炒麪
2)蓋面
3)拉麪
4)拌麪
如選擇3,則再問
1)豬肉大蔥
2)素三鮮
3)韭菜雞蛋
每項選擇後,最終會報價,如
木桶飯: 10元
如選擇4,則退出

        #!/bin/bash
        PS3="Please choose your food: "
        echo "請問吃什麼"
        caidan() {
        select menu in 飯 面 餃子 不吃;do
                case $REPLY in
                        1)     select fan in 炒飯 蓋飯 木桶飯 返回;do
                                        case $REPLY in
                                                1) echo "炒飯: 10";break 2;;
                                                2) echo "蓋飯: 12";break 2;;
                                                3) echo "木桶飯:15";break 2;;
                                                4) caidan;;
                                        esac    
                                done
                        ;;
                        2)     select mian in 炒麪 蓋面 拉麪 拌麪 返回;do
                                        case $REPLY in
                                                1) echo "炒麪: 10";break 2;;
                                                2) echo "蓋面: 12";break 2;;
                                                3) echo "拉麪:15";break 2;;
                                                4) echo "拌麪: 18";break 2;;
                                                5) caidan;;
                                        esac    
                                done        
                        ;;
                        3)    select jiaozi in 豬肉大蔥 素三鮮 韭菜雞蛋 返回;do
                                        case $REPLY in
                                                1) echo "豬肉大蔥: 10";break 2;;
                                                2) echo "素三鮮: 12";break 2;;
                                                3) echo "韭菜雞蛋:15";break 2;;
                                                4) caidan;;
                                        esac    
                                done
                        ;;
                        4) exit;;
                esac
        done
        }
        caidan


信號捕捉trap
trap '觸發指令' 信號
自定義進程收到系統發出的指定信號後,將執行觸發指令,而不會執行原操作
trap '' 信號
忽略信號的操作
trap '-' 信號
恢復原信號的操作
trap -p
列出自定義信號操作

trap示例

        #!/bin/bash
        trap 'echo “signal:SIGINT"' int 
        trap -p 
        for((i=0;i<=10;i++));do 
                sleep 1
                echo $i 
        done 
        trap '' int 
        trap -p 
        for((i=11;i<=20;i++));do
                sleep 1 
                echo $i 
        done 
        trap '-' int 
        trap -p 
        for((i=21;i<=30;i++));do 
                sleep 1 
                echo $i 
        done
  • 函數介紹

        函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程
        它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分
        函數和shell程序比較相似,區別在於:
        1.Shell程序在子Shell中運行
        2. 而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改
    
    定義函數
    • 函數由兩部分組成:函數名和函數體 • help function 
    • 語法一:
    function f_name
    {
    ...函數體...
    }
    • 語法二:
    function f_name ()
    {
    ...函數體...
    }
    • 語法三:
    f_name ()
    {
    ...函數體...
    }
    
        函數使用
        • 函數的定義和使用: – 可在交互式環境下定義函數 – 可將函數放在腳本文件中作爲它的一部分 – 可放在只包含函數的單獨文件中 • 調用:函數只有被調用纔會執行
        調用:給定函數名
        函數名出現的地方,會被自動替換爲函數代碼
        • 函數的生命週期:被調用時創建,返回時終止
    
        檢查載入函數
        使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函
        數

    示例:

        set
                findit=( ){
                if [ $# -lt 1 ]; the
                echo "usage :findit file";
                return 1
                fi
                find / -name $1 -print
        }…
    
        刪除shell函數
        現在對函數做一些改動後,需要先刪除函數,使其對shell不可用。使用unset命令完成刪除函數
        命令格式爲:
        unset function_name
        示例:
        unset findit
        再鍵入set命令,函數將不再顯示
        環境函數
        使子進程也可使用
        聲明:export –f function_name
        查看:export -f 或 declare -xf
    
        函數可以接受參數:
        傳遞參數給函數:調用函數時,在函數名後面以空白分隔給定參數列表即可;例如
        “testfunc arg1 arg2 ...”
        在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量
    
        馬函數變量
        變量作用域:
        環境變量:當前shell和子shell有效
        本地變量:只在當前shell進程有效,爲執行腳本會啓動專用子shell進程;因此,本地變量
        的作用範圍是當前shell腳本程序文件,包括腳本中的函數
        局部變量:函數的生命週期;函數結束時變量被自動銷燬
        注意:如果函數中有局部變量,如果其名稱同本地變量,使 用局部變量
        在函數中定義局部變量的方法
        local NAME=VALUE
  • 函數遞歸示例

        函數遞歸:
        函數直接或間接調用自身
        注意遞歸層數
        遞歸實例:
        階乘是基斯頓·卡曼於 1808 年發明的運算符號,是數學術語
        一個正整數的階乘(factorial)是所有小於及等於該數的正整數的積,並且有0的階乘爲1,自然數n的
        階乘寫作n!
        n!=1×2×3×...×n階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n
        n!=n(n-1)(n-2)...1
        n(n-1)! = n(n-1)(n-2)!
    
        #!/bin/bash
        #fact() {
                if [ $1 -eq 0 -o $1 -eq 1 ]; then
                echo 1
                else
                echo $[$1*$(fact $[$1-1])]
                fi
        }
        fact $1
    
        ×××
        fork×××是一種惡意程序,它的內部是一個不斷在fork進程的無限循環,實質是一個簡單的遞歸
        序。由於程序是遞歸的,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統裏面的所有資
        源
        函數實現
        :(){ :|:& };:
        bomb() { bomb | bomb & }; bomb
        腳本實現
        cat Bomb.sh
        #!/bin/bash
        ./$0|./$0&
  • 數組

        變量:存儲單個元素的內存空間
        數組:存儲多個元素的連續的內存空間,相當於多個變量的集合
        數組名和索引
        索引:編號從0開始,屬於數值索引
        注意:索引可支持使用自定義的格式,而不僅是數值格式,即爲關聯
        索引,bash4.0版本之後開始支持
        bash的數組支持稀疏格式(索引不連續)
        聲明數組:
        declare -a ARRAY_NAME
        declare -A ARRAY_NAME: 關聯數組
        注意:兩者不可相互轉換

數組賦值

        數組元素的賦值:
        (1) 一次只賦值一個元素;
        ARRAY_NAME[INDEX]=VALUE
        weekdays[0]="Sunday
        weekdays[4]="Thursday
        (2) 一次賦值全部元素:
        ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
        (3) 只賦值特定元素:
        ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
        (4) 交互式數組值對賦值
        read
        -a ARRAY
        顯示所有數組:declare
        -a

        引用數組
        引用數組元素:
        ${ARRAY_NAME[INDEX]}
        注意:省略[INDEX]表示引用下標爲
        0的元素
        引用數組所有元素:
        ${ARRAY_NAME[*]}
        ${ARRAY_NAME[@]}
        數組的長度
        (數組中元素的個數
        )
        :
        ${#ARRAY_NAME[*]}
        ${#ARRAY_NAME[@]}
        刪除數組中的某元素:導致稀疏格式
        unset ARRAY[INDEX]
        刪除整個數組

        unset ARRAY
        數組數據處理
        引用數組中的元素:
        數組切片:${ARRAY[@]:offset:number}
        offset: 要跳過的元素個數
        number: 要取出的元素個數
        取偏移量之後的所有元素
        ${ARRAY[@]:offset}
        向數組中追加元素:
        ARRAY[${#ARRAY[*]}]=value
        關聯數組:
        declare -A ARRAY_NAME ARRAY_NAME=([idx_name1]='val1'
        [idx_name2]='val2‘...)
        注意:關聯數組必須先聲明再調用

示例
生成10個隨機數保存於數組中,並找出其最大值和最小值

        #!/bin/bash
        declare -a rand
        declare -i max=0
        declare –i min=32767
        for i in {0..9}; do
                rand[$i]=$RANDOM
                echo ${rand[$i]}
                [ ${rand[$i]} -gt $max ] && max=${rand[$i]}
                [ ${rand[$i]} -lt $min ] && min=${rand[$i]}
                done
        echo "Max: $max Min:$min"

示例
編寫腳本,定義一個數組,數組中的元素是/var/log目錄下所有以.log結尾的
文件;要統計其下標爲偶數的文件中的行數之和

        #!/bin/bash
        #declare -a files
        files=(/var/log/*.log)
        declare -i lines=0
        for i in $(seq 0 $[${#files[*]}-1]); do
                if [ $[$i%2] -eq 0 ];then
                let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1)
                fi
        done
        echo "Lines: $lines."
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章