shell編程進階

流程控制

if

單條件

if 判斷條件;then
    條件為真的分支代碼
fi

雙分支

if 判斷條件; then
    條件為真的分支代碼
else
    條件為假的分支代碼
fi  

多分支

if 判斷條件1; then
    條件為真的分支代碼
elif判斷條件2; then
    條件為真的分支代碼
elif判斷條件3; then
    條件為真的分支代碼
else
    以上條件都為假的分支代碼
fi

case

case支持glob風格的通配符:

*: 任意長度任意字符
?: 任意單個字符
[]:指定范圍內的任意單個字符
a|b: a或b

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

編寫一個腳本,實現執行它,則會顯示當前的日期,格式為:2018年06月14日 15時30分18秒 星期四

#!/bin/bash

w=$(date +%w)
         case $w in
                1) week="一";;
                2) week="二";;
                3) week="三";;
                4) week="四";;
                5) week="五";;
                6) week="六";;
                *) week="日";;
         esac
echo "$(date "+%Y年%m月%d日 %H時%M分%S秒") 星期$week"

for

for 變量名 in 列表;do
    循環體
done

給系統中所有uid大于300的用戶發一封郵件,內容:"你好,用戶xxx, 端午快樂"

#!/bin/bash
for n in $(cat /etc/passwd |cut -d: -f3);do
    if [ $n -gt 300 ];then
    UserName=`id -un $n`
    echo "你好, 用戶$UserName,端午快樂" |mail -s "端午祝福" $UserName
    fi
done 

while

while CONDITION; do
    循環體
done

a=0,打印0-9

#!/bin/bash
a=0
while [ $a -lt 10 ];do
    echo $a
    a=$[$a+1]
done

until

與while相反,條件為加時循環

until CONDITION; do
    循環體
done

a=0,打印0-9

#!/bin/bash
a=0
until [ $a -gt 9 ];do
    echo $a
    a=$[$a+1]
done

continue

提前結束第N層的本輪循環,最內層為第1層

#!/bin/bash
for n in {1..10};do
    if [ $n -eq 5 ];then
        continue
    fi
echo $n
done

break

提前結束第N層循環,最內層為第1層

#!/bin/bash
for n in {1..10};do
    if [ $n -eq 5 ];then
        break
    fi
echo $n
done

shift

用于將參量列表list左移指定次數,參量列表list一旦被移動,最左端的那個參數就從列表中刪除

#!/bin/bash
while [ $# -gt 0 ];do 
    echo $*
    shift
done

編寫一個依次創建指定用戶的腳本,如/root/bin/createuser.sh zhangsan lisi wangsu

#!/bin/bash
while [ $# -gt 0 ];do
    if id $1 &> /dev/null ;then
        echo "$1 is already exist"
    else
        useradd $1
        echo "$1 created"
    fi
    shift
done

練習

  1. 編寫腳本/root/bin/createuser.sh,實現如下功能:使用一個用戶名做為參數,如果指定參數的用戶存在,就顯示其存在,否則添加之;顯示添加的用戶的id號等信息

    #!/bin/bash
    read -p "Please enter a username: " username
    
    if `id ${username} &> /dev/null`;then
            echo "User already exists"
    else
            useradd ${username}
            echo "${username} is to created"
            cat /etc/passwd |grep "^${username}:"
    fi
    
  2. 編寫腳本/root/bin/yesorno.sh,提示用戶輸入yes或no,并判斷用戶輸入的若是y,Y,Yes,YES,yES 等則顯示YES,輸入其他的顯示NO

    #!/bin/bash
    read -p "Are you agree?yes or no: " bool
    
    bool=`echo ${bool}|tr [:upper:] [:lower:]`
    
    if [[ yes =~ ${bool} ]];then
            echo "Your answer is yes"
    else
            echo "your answer is no"
    fi
    
  3. 編寫腳本/root/bin/filetype.sh,判斷用戶輸入文件路徑,顯示其文件類型(普通,目錄,鏈接,塊設備,符號設備,管道,套接字)

    #!/bin/bash
    read -p "Please enter a path: " path
    
    if `ls ${path} > /dev/null`;then
            filetype=`ls -ld ${path} |grep -o "^."`
            case ${filetype} in
                    -) echo "The file is ordinary file";;
                    d) echo "The file is directory file";;
                    l) echo "The file is link file";;
                    b) echo "The file is block file";;
                    c) echo "The file is char file";;
                    p) echo "The file is pipe file";;
                    s) echo "The file is socket file";;
                    *) echo "The file is others";;
            esac
    else
            echo "No such file or directory"
    fi
    
  4. 編寫腳本/root/bin/checkint.sh,判斷用戶輸入的參數是否為正整數

    #!/bin/bash
    read -p "Please enter a string: " string
    
    if [[ ${string} =~ ^[[:digit:]]+$ ]];then
            echo "The string is int"
    else
            echo "The string is others"
    fi
    
  5. 編寫腳本var_filetype.sh,判斷/var/目錄下所有文件的類型

    #!/bin/bash
    for filename in `ls /var`;do
    #       echo ${filename}
            filetype=`ls -ld "/var/${filename}" |grep -o "^."`
            case ${filetype} in
                    -) echo "${filename} is ordinary file";;
                    d) echo "${filename} is directory file";;
                    l) echo "${filename} is link file";;
                    b) echo "${filename} is block file";;
                    c) echo "${filename} is char file";;
                    p) echo "${filename} is pipe file";;
                    s) echo "${filename} is socket file";;
                    *) echo "${filename} is others";;
            esac
    done
    
    #!/bin/bash
    for filename in `ls /var`;do
            if [ -f /var/${filename} ];then
                    echo "${filename} is ordirary file"
            elif [ -d /var/${filename} ];then
                    echo "${filename} is directory file"
            elif [ -L /var/${filename} ];then
                    echo "${filename} is link file"
            elif [ -b /var/${filename} ];then
                    echo "${filename} is block file"
            elif [ -c /var/${filename} ];then
                    echo "${filename} is char file"
            elif [ -p /var/${filename} ];then
                    echo "${filename} is pipe file"
            elif [ -S /var/${filename} ];then
                    echo "${filename} is socket file"
            else
                    echo "${filename} is others"
        
            fi  
    done
    
    #!/bin/bash
    
    for file in $(find /var);do
        if [ -f $file ]; then
            echo "$file is a regular file"
        elif [ -b $file ]; then
            echo "$file is a block file"
        elif [ -c $file ]; then
            echo "$file is a character file"
        elif [ -d $file ]; then
            echo "$file is a directory"
        elif [ -h $file ]; then
            echo "$file is a symbolic link"
        elif [ -p $file ]; then
            echo "$file is a named pipe"
        elif [ -S $file ]; then
            echo "$file is a socket"
        fi
    done
    
  6. 添加10個用戶user1-user10,密碼為8位隨機字符

    #!/bin/bash
    
    for n in $(seq 1 10);do
        useradd user$n &> /dev/null
        password=$(cat /dev/urandom |tr -cd [[:alpha:][:punct:]] |head -c8)
        echo ${password} | passwd --stdin user$n &> /dev/null
        echo "user$n add successed, password is ${password}"
    done
    
  7. 編寫腳本rc3.d.sh,查看/etc/rc.d/rc3.d目錄下分別有多個以K開頭和以S開頭的文件;分別讀取每個文件,以K開頭的輸出為文件加stop,以S開頭的輸出為文件名加start,如K34filename stop S66filename start

    #!/bin/bash
    
    echo "Top K is `ls /etc/rc.d/rc3.d/|grep "^K"|wc -l`"
    echo "Top S is `ls /etc/rc.d/rc3.d/|grep "^S"|wc -l`"
    
    for filename in `ls /etc/rc.d/rc3.d/`;do
            top=`echo ${filename}|grep -o "^."`
            if [ ${top} == K ];then
                    echo "${filename} stop"
            elif [ ${top} == S ];then
                    echo "${filename} start"
            else
                    continue
            fi  
    done
    
    #!/bin/bash
    
    echo "Top K is `ls /etc/rc.d/rc3.d/|grep "^K"|wc -l`"
    echo "Top S is `ls /etc/rc.d/rc3.d/|grep "^S"|wc -l`"
    
    ls /etc/rc.d/rc3.d/|sed -n "s/^K.*/& stop/p"
    ls /etc/rc.d/rc3.d/|sed -n "s/^S.*/& start/p"
    
  8. 編寫腳本sum.sh,提示輸入正整數n的值,計算1+2+…+n的總和

    #!/bin/bash
    read -p "Please enter a int: " int
    
    sum=0
    
    for i in `seq 1 $int`;do
            let sum+=i
    done
    
    echo $sum
    
    #!/bin/bash
    read -p "Please enter a int: " int 
    
    seq -s + 1 $int|bc
    
  9. 編寫腳本int3.sh,計算100以內所有能被3整除的整數之和

    #!/bin/bash
    sum=0
    
    for i in `seq 1 100`;do
            if [ `echo $[${i}%3]` == 0 ];then
                    sum=$[$sum+$i]
            else
                    continue
            fi  
    done
    
    echo $sum
    
  10. 編寫腳本hoststate.sh,提示請輸入網絡地址,如192.168.0.1,判斷輸入的主機在線狀態

    #!/bin/bash
    read -p "Please enter a IPv4 address: " ip
    
    if `ping -c1 -w1 ${ip} &> /dev/null`;then
            echo "The host is up"
    else
            echo "The host is down"
    fi
    
  11. 編寫腳本99.sh,打印九九乘法表

    #!/bin/bash
    for i in `seq 1 9`;do
            for j in `seq 1 ${i}`;do
                    echo -e "$i*$j=$[$i*$j]\t\c"
            done
            echo
    done
    
  12. 編寫腳本createhtml.sh,在/testdir目錄下創建10個html文件,文件名格式為數字N(從1到10)加隨機8個字母,如:1AbCdeFgH.html

    #!/bin/bash
    
    mkdir /testdir &> /dev/null
    
    for i in {1..10};do
       random_string=$(cat /dev/urandom |tr -cd [:alpha:] |head -c8)
       touch /testdir/$i${random_string}.html
    done
    
  13. 編寫triangle.sh,腳本打印等腰三角形

    #!/bin/bash
    read -p "請輸入需要生成的等腰三角形的行數:" zongline
    for curline in $(seq 1 $zongline);do
           spacenum=$[$zongline-$curline]
           anum=$[2*$curline-1]
           
           for kongge in $(seq 1 $spacenum);do
                   echo -e "" "\c" 
           done
           
           for a in $(seq 1  $anum);do
                   echo -e "a\c"
           done
           
           echo
    done 
    
  14. 編寫腳本caipiao.sh,填5個數字,猜中多少按以下方式輸出
    0 3 5 9 3
    如果猜中一個,則輸出First blood
    如果猜中兩個,則輸出Double kill
    如果猜中三個,則輸出Triple kill
    如果猜中四個,則輸出Quadra kill
    如果全中,則輸出“大吉大利,今晚吃雞”

     #!/bin/bash
     read -p "Please enter a number: " a
     read -p "Please enter a number: " b
     read -p "Please enter a number: " c
     read -p "Please enter a number: " d
     read -p "Please enter a number: " e
     
     A=`echo $[$RANDOM%10]`
     B=`echo $[$RANDOM%10]`
     C=`echo $[$RANDOM%10]`
     D=`echo $[$RANDOM%10]`
     E=`echo $[$RANDOM%10]`
     
     echo "$A$B$C$D$E"
     
     a=`[ $a -eq $A ];echo $?`
     b=`[ $b -eq $B ];echo $?`
     c=`[ $c -eq $C ];echo $?`
     d=`[ $d -eq $D ];echo $?`
     e=`[ $e -eq $E ];echo $?`
     
     i=$[$a+$b+$c+$d+$e]
     
     case $i in
             5) echo "No kill";;
             4) echo "First blood";;
             3) echo "Double kill";;
             2) echo "Triple kill";;
             1) echo "Quadra kill";;
             0) echo "大吉大利,今晚吃雞"
     esac
    
    #!/bin/bash
    
    lucky_num=$(cat /dev/urandom | head -100|tr -c [:digit:] "@" |tr -d @|cut -c1-5)
    
    echo "The lucky number has been generated"
    read -p "Please enter your results: " input_num
    
    echo "lucky_num is $lucky_num"
    echo "input_num is $input_num"
    
    get_num_lens=0
    for i in `seq 1 5`;do
       r1=`echo $lucky_num|grep -o "[[:digit:]]"| sed -n "$i p"`
       r2=`echo $input_num|grep -o "[[:digit:]]"| sed -n "$i p"`
       if [ $r1 -eq $r2 ];then
           get_num_lens=$[$get_num_lens+1]
       fi
    done
    
    case $get_num_lens in
       1) echo "First blood";;
       2) echo "Double kill";;
       3) echo "Triple kill";;
       4) echo "Quadra kill";;
       5) echo "大吉大利,今晚吃雞";;
       *) echo "sorry~";;
    esac
    
  15. 編寫腳本,求100以內所有正奇數之和

    #!/bin/bash
    sum=0
    for i in `seq 1 100`;do
            if [ $[$i%2] -eq 1 ];then
                    sum=$[$sum+$i]
            fi
    done
    echo sum=$sum
    
  16. 編寫腳本,提示請輸入網絡地址,如192.168.0.0,判斷輸入的網段中主機在線狀態,并統計在線和離線主機各多少

  17. 編寫腳本,打印九九乘法表

  18. 編寫腳本,利用變量RANDOM生成10個隨機數字,輸出這個10數字,并顯示其中的最大值和最小值

    #!/bin/bash
    for i in `seq 1 10`;do
            j=$RANDOM
            echo $j
            if [ $i == 1 ];then
                    max=$j
                    min=$j
            else
                    if [ $j -ge $max ];then
                            max=$j
                    fi
                    if [ $j -le $min ];then
                            min=$j
                    fi
            fi
    done
    echo max:$max
    echo min:$min
    
  19. 編寫腳本,實現打印國際象棋棋盤

    #!/bin/bash
    for i in {1..8};do
            for j in {1..8};do
                    if [ $[$i%2] == 1 ];then
                            if [ $[$j%2] == 1 ];then
                                    echo -e "\033[47;37m  \033[0m\c"
                            else
                                    echo -e "\033[40;37m  \033[0m\c"   
                            fi
                    else
                            if [ $[$j%2] == 1 ];then
                                    echo -e "\033[40;37m  \033[0m\c"   
                            else
                                    echo -e "\033[47;37m  \033[0m\c"
                            fi
                    fi
            done
            echo
    done
    
  20. 后續六個字符串:efbaf275cd. 4be9c40b8b. 44b2395c46. f8c8873ce0. b902c16c8b. ad865d2f63是通過對隨機數變量RANDOM隨機執行命令:
    echo $RANDOM|md5sum|cut –c1-10
    后的結果,請破解這些字符串對應的RANDOM值

    #!/bin/bash
    i=0
    while [ $i -le 32767 ];do
            str=`echo $i|md5sum|cut -c1-10`
    #       echo $str
            case $str in
                    efbaf275cd) echo "efbaf275cd:$i";;
                    4be9c40b8b) echo "4be9c40b8b:$i";;
                    44b2395c46) echo "44b2395c46:$i";;
                    f8c8873ce0) echo "f8c8873ce0:$i";;
                    b902c16c8b) echo "b902c16c8b:$i";;
                    ad865d2f63) echo "ad865d2f63:$i";;
            esac
            i=$[$i+1]
    done
    
  21. 隨機生成10以內的數字,實現猜字游戲,提示比較大或小,相等則退出

    #!/bin/bash
    random=$[$RANDOM%10]
    while true;do
            read -p "Please enter a int less than 10: " i
            if [ $i -eq $random ];then
                    echo "bingo"
                    break
            elif [ $i -gt $random ];then
                    echo "greater"
            else
                    echo "less"
            fi
    done
    
  22. 用文件名做為參數,統計所有參數文件的總行數

    #!/bin/bash
    sum=0
    while [ $# -gt 0 ];do
        lines=`cat $1|wc -l`
        shift
        sum=$[$sum+$lines]
    done
    echo $sum
    
  23. 用二個以上的數字為參數,顯示其中的最大值和最小值

特殊用法

while

依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將行賦值給變量xxx

while read xxx; do
    循環體
done < /PATH/FROM/SOMEFILE

將COMMAND執行的結果依次讀取然后將每行依次賦值給變量xxx

COMMAND |while read xxx; do
    循環體
done

掃描/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

找出分區利用率大于10%的分區

#!/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

for ((i=1;i<=100;i++));do
        let sum+=i
done
echo sum=$sum

for

for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式));do
    循環體
done

select

主要用于創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,并顯示PS3 提示符,等待用戶輸入。用戶輸入菜單列表中的某個數字,執行相應的命令。用戶輸入被保存在內置變量REPLY中。常于case配合

select variable in list;do
    循環體命令
done

設計點菜系統,列出主菜單,選擇后選擇后,提示用戶,要選幾份,輸入完后提示是否還要繼續選,如繼續選,返回到主菜單,然后繼續選,如不要繼續選,則會打印消費單后退出

#!/bin/bash
PS3="Please choose your food: "
echo > /tmp/list
echo "請問吃什么"

getBill() {
    for i in $(cat /tmp/list |cut -d: -f4|tr -d 元);do
        let total+=i 
    done
    echo -e "\n總計:${total}元" >> /tmp/list
    cat /tmp/list
}

getCount() {
    read -p "Please enter count(int): " count
    sum=$[$(echo $price|cut -d: -f2)*$count]
    echo "${price}元:${count}份:${sum}元" >> /tmp/list
    while true;do
        select ack in $*;do
            case $REPLY in
                1)  break 2
                ;;
                2)  getBill
                    exit
                ;;
            esac
        done
    done
}

getPrice() {
    while true;do
        select price in $*;do
            case $REPLY in
                1)  echo $price;
                    getCount "繼續點菜" "結算"
                    break 3
                ;;
                2)  echo $price;
                    getCount "繼續點菜" "結算"
                    break 3
                ;;
                3)  echo $price;
                    getCount "繼續點菜" "結算"
                    break 3
                ;;
                4)  break 3;;
            esac
        done
    done
}

while true;do
    select menu in 飯 面 餃子 不吃 結算;do
        case $REPLY in
            1)  getPrice "炒飯:10" "蓋飯:12" "木桶飯:15" "返回"
            ;;
            2)  getPrice "炒面:15" "蓋面:16" "拉面:10" "返回"
            ;;
            3)  getPrice "豬肉大蔥:20" "素三鮮:15" "韭菜雞蛋:18" "返回"
            ;;
            4)  exit
            ;;
            5)  getBill
                exit
            ;;
        esac
    done
done

trap

信號捕捉

trap '觸發指令' 信號:自定義進程收到系統發出的指定信號后,將執行觸發指令,而不會執行原操作

trap '' 信號:忽略信號的操作

trap '-' 信號:恢復原信號的操作

trap -p:列出自定義信號操作

#!/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 f_name{
...函數體...
}

語法二

function f_name(){
...函數體...
}

語法三

f_name(){
...函數體...
}

返回值

默認默認取決于函數中執行的最后一條命令的退出狀態碼

也可以通過return自定義狀態碼,0表示無錯誤返回,1-255表示由錯誤返回

定義

交互式環境下定義
將函數放在腳本文件中作為它的一部分
可放在只包含函數的單獨文件中

載入函數文件

./source PATH/SOMEFILE:載入指定函數文件

set function_name:檢查函數是否已載入當前shell
unset function_name:刪除當前shell中的此函數

分類

內部函數:變量僅對當前函數有效,local定義局部變量
全局函數:變量對當前shell有效,默認函數中的變量為全局變量
環境函數:變量對當卡nshell及子shell有效,export -f/declare -xf定義全局變量

參數

在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量

#!/bin/bash
func() {
    echo func:1st is $1
    echo func:2nd is $2
    echo func:all are $*
    echo func:the arg number is $#
    echo func:the func name is $0
    shift
    echo func:1st is $1
}
func a b c d 

數組

聲明

默認定義數組時,若不聲明數組的類型,則設置為索引數組,若不指定索引,則用0,1,2,3,4...

declare -a ARRAY_NAME:聲明數值索引數組
declare -A ARRAY_NAME: 聲明關聯索引數組,可以自定義索引格式

注意:兩者不可相互轉化

declare -a:顯示所有數值索引數組
declare -A:顯示所有關聯索引數組

[root@centos6 ~]# declare -A height=([zhangsan]=180 [lisi]=170 [wangwu]=158)

賦值

ARRAY_NAME[INDEX]=VALUE

ARRAY_NAME=(VAL1 VAL2 VAL3 ...)

ARRAY_NAME=([0]=VAL1 [3]=VAL2 ...)

read -a ARRAY

引用

${ARRAY_NAME[INDEX]}:引用數組元素

${ARRAY_NAME[*]}:引用數組所有元素

${#ARRAY_NAME[*]}:數組的長度(數組中元素的個數)

unset ARRAY[INDEX]:刪除數組中的某元素,導致稀疏格式

unset ARRAY:刪除整個數組

數據處理

${ARRAY[*]:offset:number}:數組切片,offset表示要跳過的元素個數,number表示要取出的元素個數

ARRAY[${#ARRAY[*]}]=value:向數組中追加元素

[root@centos6 ~]# declare -a|grep name
declare -a name='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
[root@centos6 ~]# echo ${name[*]:2:3}
3 4 5

示例

寫一腳本會生成一個名為digit的數組,包含10個隨機數,再顯示最大值,最小值

#!/bin/bash
getArr() {
    for((i=0;i<$1;i++));do
        random[$i]=$RANDOM
    done
    echo ${random[*]}
}

getMax() {
    while [ $# -gt 0 ];do
        if [ -z $max ];then
            max=$1
        else
            [ $1 -gt $max ] && max=$1
        fi
    shift
    done
    echo $max
}

getArr 10
getMax ${random[*]}
#!/bin/bash
for i in {0..9}
do
    array_random[$i]=$RANDOM
done

echo ${array_random[*]}

max_num() {
    maxnum=${array_random[0]}
    for i in {1..9}
    do
        if [ ${array_random[$i]} -gt $maxnum ];then
            maxnum=${array_random[$i]}
        fi
    done

    echo '最大值:' $maxnum
}

min_num() {
    minnum=${array_random[0]}

    for i in {1..9}
    do
        if [ ${array_random[$i]} -lt $minnum ];then
            minnum=${array_random[$i]}
        fi
    done

    echo '最小值:' $minnum
}

max_num
min_num

定義一個數組,數組中的元素是/var/log目錄下所有以.log結尾的文件,并統計其下標為偶數的文件中的行數之和

#!/bin/bash
arrLog=(/var/log/*.log)

for n in $(seq 0 $[${#arrLog[*]}-1]);do
    if [ $[$n%2] -eq 0 ];then
        echo ${arrLog[$n]}
        let lines+=$(cat ${arrLog[$n]}|wc -l) # let lines+=$(wc -l < ${array[$i]})
    fi
done
echo "lines: $lines"

練習

  1. 編寫函數,實現OS的版本判斷

    #!/bin/bash
    judge_version() {
            version=$(cat /etc/redhat-release |sed -n "s/.* \([0-9]\)\..*/\1/p")
            [ $version == 6 ] && return 0 || return 1
    }
    judge_version
    
  2. 編寫函數,實現取出當前系統eth0的IP地址

    #!/bin/bash
    get_ipv4() {
            ip=$(ifconfig $1| sed -n "s/[[:space:]]\+inet addr:\([0-9\|.]\+\) .*/\1/p")
            return 0
    }
    get_ipv4 eth0
    
  3. 編寫函數,實現打印綠色OK和紅色FAILED

    #!/bin/bash
    . /etc/init.d/functions
    
    print_green_ok() {
     action $1 true
    }
    
    print_red_failed() {
     action $1 false
    }
    
    print_green_ok nihao
    print_red_failed nihao
    
  4. 編寫函數,實現判斷是否無位置參數,如無參數,提示錯誤

    #!/bin/bash
    judge_arguments() {
     [ $1 == 0 ] && echo "Please enter arguments"
    }
    judge_arguments $#
    
  5. 編寫服務腳本/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_NAME is running...”
    如果/var/lock/subsys/SCRIPT_NAME文件不存在,則顯示“SCRIPT_NAME is stopped...”
    其中:SCRIPT_NAME為當前腳本名

    #!/bin/bash
    srv_name=$(basename $0)
    
    srv_find() {
     find /var/lock/subsys/$srv_name &> /dev/null    
    }
    srv_echo() {
     echo "$srv_name is $1"
    }
    srv_start() {
     if srv_find;then
         srv_echo "running..."
     else
         touch /var/lock/subsys/$srv_name
         srv_echo "start"
     fi
    }
    
    srv_stop() {
     if srv_find;then
         rm -rf /var/lock/subsys/$srv_name 
         srv_echo "stop"
     else
         src_echo "stopped..."
     fi
    }
    
    srv_restart() {
     if srv_find;then
         rm -rf /var/lock/subsys/$srv_name 
         touch /var/lock/subsys/$srv_name 
     else
         touch /var/lock/subsys/$srv_name 
     fi
     srv_echo "restart"
    }
    
    srv_status() {
     srv_find && srv_echo "running..." || srv_echo "stopped..."
    }
    main() {
     case $1 in
         start)  srv_start
         ;;
         stop)   srv_stop
         ;;
         restart)srv_restart
         ;;  
         status) srv_status
         ;;
         *)  echo "Please enter a argument: start or stop or restart or status"
             exit
         ;;
     esac
    }
    
    main $1
    
  6. 編寫腳本/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退出

    #!/bin/bash
    
    get_path() {
     which $1|grep -o "/.*/$1$"
    }
    
    mkdir_all() {
     cd $2
     for i in $(dirname $1|tr / ' ');do
         mkdir $i &> /dev/null
         cd $i
     done
    }
    cp_cmd() {
     mkdir_all $(get_path $1) $2
     cp $(get_path $1) .
     for n in $(ldd $(get_path $1)|grep -o "/[^[:space:]]\+");do
         echo $n
         mkdir_all $n $2
         p $n .
     done
    }
    
    while true;do
     read -p "Please enter a executable cmd name or quit: " cmd
     [ $cmd == quit ] && exit
     if $(which $cmd &> /dev/null);then
         cp_cmd $cmd /app/nihao  
     else
         echo "Your answer is not a executable cmd"
     fi
    done
    
  7. 編寫函數實現兩個數字做為參數,返回最大值

    #!/bin/bash
    get_max() {
     [ $1 -gt $2 ] && max=$1 || max=$2
     [ $max -gt 255 ] && echo "Please enter a 0~255 int" || return $max
    }
    
    get_max $1 $2
    echo $?
    
  8. 斐波那契數列又稱黃金分割數列,因數學家列昂納多·斐波那契以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……,斐波納契數列以如下被以遞歸的方法定義:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
    利用函數,求n階斐波那契數列

    
    
  1. 輸入若干個數值存入數組中,采用冒泡算法進行升序或降序排序

    #!/bin/bash
    declare -A arr # 注意聲明位置,在函數內聲明就只作用在函數內
    get_arr() {
     for n in $(seq 1 $1);do
         index=$RANDOM
         arr[$index]=$index
     done
     echo ${arr[*]}
    }
    get_sort() {
     while [ ${#arr[*]} -gt 1 ];do
         max=0
         for i in ${arr[*]};do  # 注意刪除元素后其他元素的索引不變
             [ $i -gt $max ] && max=$i
         done
         echo $max
         unset arr[$max]
     done
     echo ${arr[*]}
    }
    
    get_arr 10
    get_sort
    
  2. 將下圖所示,實現轉置矩陣matrix.sh,將列轉換成行顯示
    1 2 3 1 4 7
    4 5 6 ===> 2 5 8
    7 8 9 3 6 9

#!/bin/bash
get_matrix() {
 for i in $(seq 1 $1);do
     for j in $(seq 1 $2);do
         eval "arr$i[$j]=$[$RANDOM%10]"
     done
     eval echo "\${arr$i[*]}"
 done
}

trans_matrix() {
 for m in $(seq 1 $2);do
     for n in $(seq 1 $1);do
         eval echo -n "\${arr$n[$m]}' '"
     done
     echo
 done    
}
get_matrix 3 6
echo
trans_matrix 3 6   

高級字符串

切片

${#var}:返回字符串變量var的長度

${var:offset}:返回字符串變量var中從第offset個字符后(不包括第offset個字符)的字符開始,到最后的部分,offset的取值在0 到 ${#var}-1 之間(bash4.2后,允許為負值)

${var:offset:number}:返回字符串變量var中從第offset個字符后(不包括第offset個字符)的字符開始,長度為number的部分

${var: -length}:取字符串的最右側幾個字符,注意:冒號后必須有一空白字符

${var:offset:-length}:從最左側跳過offset字符,一直向右取到距離最右側lengh個字符之前的內容

${var: -length:-offset}:先從最右側向左取到length個字符開始,再向右取到距離最右側offset個字符之間的內容,注意:-length前空格

取子串

${var#*word}:其中word可以是指定的任意字符,自左而右,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串開頭至第一次出現word字符之間的所有字符

${var##*word}:同上,貪婪模式,不同的是,刪除的是字符串開頭至最后一次由word指定的字符之間的所有內容

${var%word*}:其中word可以是指定的任意字符,自右而左,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串最后一個字符向左至第一次出現word字符之間的所有字符

${var%%word*}:同上,只不過刪除字符串最右側的字符向左至最后一次出現word字符之間的所有字符

查找替換

${var/pattern/substr}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替換之

${var//pattern/substr}: 查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替換之

${var/#pattern/substr}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替換之

${var/%pattern/substr}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替換之

${var/pattern}:刪除var表示的字符串中第一次被pattern匹配到的字符串

${var//pattern}:刪除var表示的字符串中所有被pattern匹配到的字符串

${var/#pattern}:刪除var表示的字符串中所有以pattern為行首匹配到的字符串

${var/%pattern}:刪除var所表示的字符串中所有以pattern為行尾所匹配到的字符串

轉換大小寫

${var^^}:把var中的所有小寫字母轉換為大寫

${var,,}:把var中的所有大寫字母轉換為小寫

高級變量

變量賦值.jpg

declare

聲明指定類型的變量,常用選項:

  • -r:聲明或顯示只讀變量
  • -i:聲明或顯示整數型變量
  • -a:聲明或顯示索引數組
  • -A:聲明或顯示關聯數組
  • -f:顯示已定義的所有函數名
  • -F:僅顯示已定義的所有函數名
  • -x:聲明或顯示環境變量和函數
  • -l:聲明變量為小寫字母
  • -u:聲明變量為大寫字母

eval

eval命令將會首先掃描命令行進行所有的置換,然后再執行該命令。該命令適用于那些一次掃描無法實現其功能的變量.該命令對變量進行兩次掃描

eval tempvar=$$variable1
tempvar=${!variable1}

[root@server ~]# CMD=whoami
[root@server ~]# echo $CMD
whoami
[root@server ~]# eval $CMD
root
[root@server ~]# n=10
[root@server ~]# echo {0..$n}
{0..10}
[root@server ~]# eval echo {0..$n}
0 1 2 3 4 5 6 7 8 9 10

[root@server ~]# N=NAME
[root@server ~]# NAME=fanjie
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
fanjie

mktemp

創建并顯示臨時文件,可避免沖突,常用選項:

  • -d:創建并顯示臨時目錄
[root@centos6 ~]# mktemp /app/tempXX
mktemp: too few X's in template `/app/tempXX'
[root@centos6 ~]# mktemp /app/tempXXX
/app/tempmEL
[root@centos6 ~]# mktemp
/tmp/tmp.7ey38eMKh8

[root@centos6 ~]# mktemp -d /app/tempXXXXXXXX
/app/tempYb2pAQ7c
[root@centos6 ~]# mktemp -d
/tmp/tmp.HHoPgpEe4K

install

安裝復制文件,可指定權限屬主和屬組,install [OPTION]... SOURCE... DIRECTORY,常用選項:

  • -T SOURCE DEST:單文件
  • -t DIRECTORY SOURCE
  • -d DIRECTORY...:創建空目錄
  • -m MODE:指定權限,默認是755
  • -o OWNER:指定屬主
  • -g GROUP:指定數組
[root@centos6 ~]# install -m 700 -o fanjie -g admins /etc/passwd /app/
[root@centos6 ~]# install –m 770 –d /testdir/installdir

expect

expect 是由Don Libes基于Tcl( Tool Command Language )語言開發的,主要應用于自動化交互式操作的場景,借助Expect處理交互的命令,可以將交互過程如:ssh登錄,ftp登錄等寫在一個腳本上,使之自動化完成。尤其適用于需要對多臺服務器執行相同操作的環境中,可以大大提高系統管理人員的工作效率,常用選項:

  • -c:從命令行執行expect腳本,默認expect是交互地執行的
  • -d:可以輸出輸出調試信息

expect中相關命令

spawn:啟動新的進程
send:用于向進程發送字符串
expect:從進程接收字符串
interact:允許用戶交互
exp_continue:匹配多個字符串在執行動作后加此命令

#!/usr/bin/expect
spawn scp /etc/fstab 192.168.8.100:/app
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send “magedu\n" }
}
expect eof

#!/usr/bin/expect
spawn ssh 192.168.8.100
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send “magedu\n" }
}
interact
#expect eof

#!/usr/bin/expect
set ip 192.168.8.100
set user root
set password magedu
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact

#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact
#./ssh3.exp 192.168.8.100 root magedu

#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo magedu |passwd --stdin haha\n" }
send "exit\n"
expect eof
#./ssh4.exp 192.168.8.100 root magedu

#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo magedu |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
#./ssh5.sh 192.168.8.100 root magedu
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容