bash腳本編程之判斷和循環

bash腳本編程之判斷和循環

     變量:
      特性:一個變量中只能存儲一個數值;

     數組:能夠容納多個數組元素的連續的內存空間;

      1)稀疏數組:
      2)稠密數組: 

    數組元素:數組中任何一個存放數據的存儲單元,其作用相當於一個變量;

    數組元素的標識:

      索引數組標識:所有的數組元素都是使用數字編號的;
      通常數字標號是從0開始的,即:0,1,2....

    關聯數組標識:所有的數組元素都可以使用名稱(字符串)來標識;
      注意:bash4.0以上版本纔有可能支持關聯數組;

   數組聲明定義:
      1.declare命令:
         -a to make NAMEs indexed arrays (if supported)
           將其後的變量名稱聲明爲索引數組;declare -a names=([0]='z'[1]='w' [2]='1')
         -A to make NAMEs associative arrays (if supported)
           將其後的變量名稱聲明爲關聯數組;declare -a names=( "z" "w" "l" )

     2.直接使用變量賦值的方式:
         定義稠密的索引數組
         ARRAY-NAME=(“VALUE1” “VALUE2”....)

         定義稀疏數組:
          ARRAY-NAME=([0]=“VALUE1” [1]=“VALUE2”....)

         定義關聯數組:
           ARRAY-NAME=(INDEX_name1='VALUE1' INDEX_VALUE2='VALUE2'....)

     3.分別定義數組元素:
         ARRAY_NAME[0]='VALUE' 
         ARRAY_NAME[1]='VALUE'      
         ARRAY_NAME[2]='VALUE'
         .......

  引用數組元素的方式:
        ${ARRAY_NAME[INDEX]} 
        注意:在引用數組元素時,沒人給出索引號,默認編號爲”0“,即顯示第一個數組元素的數值;

  引用整個數組中所有元素:
        ${ARRAY_NAME[*]}或 ${ARRAY_NAME[@]} 

  引用整個數組的所有元素的索引號:
        ${!ARRAY_NAME[*]}或 ${!ARRAY_NAME[@]}

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

  數組切片:
        ${ARRAY_NAME[*]:offset}
          //顯示包括offste數值所對錶示的位置的元素極其後所有的元素;

        ${ARRAY_NAME[*]:offset:number}
          //顯示數組中包括offset數值所對應表示的位置的元素及其後number個元素;

  數組撤銷:
        unset ARRAY_NAME

  RANDOM變量:
        隨機數變量:0-32767,整數值;

        從熵池中取隨機數;

        熵池:
        /dev/random
          兩次敲擊鍵盤的時間間隔;
          兩次IO的時間間隔;
          ...
        /dev/urandom:僞熵池
          利用應用程序計算得到的隨機數;
                        [root@localhost ~]# echo $RANDOM
         25570
         [root@localhost ~]# echo $RANDOM
         8882
         [root@localhost ~]# echo $RANDOM
         2373
        [root@localhost ~]# echo $RANDOM
        4870
       [root@localhost ~]# echo $RANDOM
        3895
       [root@localhost ~]# echo $RANDOM
        8353
      [root@localhost ~]# echo $RANDOM
       14099

 bash腳本編程的結構:
   bash腳本編程語言:
        腳本類語言
        解釋型語言
        過程式編程語言

      過程式編程語言結構:
       順序執行結構:默認
         從上而下,自左而右地執行所有的語句(命令);

       選擇執行結構:
         當條件滿足或不足時,纔會執行對應的語句(命令);

       循環執行結構:
         重複執行某段語句(命令);    

   bash腳本編程語中也具有上述結構;
        順序執行結構:默認

       選擇執行結構:
        根據給定條件邏輯判斷結果或根據某個可選取的取值範圍,進而選擇某個分支結構中的命令語句予以執行的方式;
       if:
         選擇執行結構的標準,根據條件的邏輯判斷結果選擇執行的語句內容;
       case:
         選擇執行結構的標準,根據符合某特定範圍的取值標準選擇執行的語句內容;

       循環執行結構:
        對於特定語句內容 ,重複執行0,1,多次;

       for:以遍歷列表的方式進行循環;
       while:根據給定條件的邏輯判斷結果進行循環,邏輯判斷結果爲真,循環,否則,停止循環;   
       until:根據給定條件的邏輯判斷結果進行循環,邏輯判斷結果爲假,循環,否則,停止循環;
       select:死循環,即沒有默認退出條件的循環;利用循環提供一個可選擇的列表;

 bash腳本的執行結構之if選擇執行結構:
      if語句:if - if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi

    if語句的單分支結構:
        if - if COMMANDS; then COMMANDS; fi

        注意:是否會執行then後面的命令,取決於if後面的命令的執行狀態返回值:
            1.如果其返回值爲真,則執行then後面的命令;
            2.如果其返回值爲假,則不執行then後面的命令;

      建議在腳本中的書寫格式:
       if CONDITION ; then
         statement
          ...
       fi 

       或

       if  CONDITION

       then

         STATEMENT
         ....
       fi

       if語句的雙分支結構:
        if - if COMMANDS; then COMMANDS; else COMMANDS; fi

        注意:是否執行then後的命令或else後的命令,取決於if後的命令執行狀態的返回值;
        1.如果返回值爲真,則執行then後的命令;
        2.如果爲假,則執行else後的命令;

       if語句的多分支結構:

         if - if COMMANDS; then COMMANDS; else COMMANDS; fi

        注意:是否執行then後的命令或else後的命令,取決於if後的命令執行狀態的返回值或else後的命令執行狀態返回值;

        1.首先判斷if後的命令的狀態返回值是否爲真,爲真就執行then後的語句;如果爲假,就繼續判斷第一個elif後的命令
          執行狀態返回值;
        2.第一個elif後的命令執行狀態返回值爲真,就執行第一個elif語句中then後的命令,否則,就繼續判斷第二個elif後的
          執行狀態返回值;
       3.以此類推,會判斷每個elif後面的命令的執行狀態返回值是否爲真;如果所有的if和elif後面的命令的執行狀態返回值均爲假,則執行else後面的語句;

        建議在腳本中的書寫格式:
            if CONDITION1 ; then
                STATEMENT
                ...
            elif CONDITION2 ; then
                STATEMENT
                ...
            elif CONDITION3 ; then
                STATEMENT
                ...
            ...
            else
                STATEMENT
                ...
            fi
            或
            if CONDITION
            then
                STATEMENT
                ...
            elif CONDITION2 ; then
                STATEMENT
                ...
            elif CONDITION3 ; then
                STATEMENT
                ...
            ...
            else
                STATEMENT
                ...
            fi

       注意:if的多分支結構,使用場景不多,而且有時候,可以使用嵌套的多分支或雙分支if結構代替if多分支結構;

       嵌套的if結構
         if CONDITION1 ; then
            if CONDITION2 ; then
               if CONDITION3 ; then
                STATEMENT
                ...

               else
                STATEMENT
                ...

            else
             STATEMENT
              ...
              fi

         else
          STATEMENT
          ...
          fi

     COMMAND1 && COMMAND2 || COMMAND3

    示例:
      寫腳本,判斷某個用戶的默認登錄shell是否爲/bin/bash

      #!/bin/bash
      y=$(cut -d: -f 1 /etc/shadow | sort -R | head -1)
      w=$(egrep "^$y\>" /etc/passwd | cut -d: -f 7)
      if [ "$w"=="/bin/bash" ] ; then
          echo "${y}'s login shell is /bin/bash."
      else
          echo "${y}'s login shell is ${w}"
      fi

      unset y w

     bash腳本編程之用戶交互使用:
       位置參數變量:
       $0:命令的本身,對於腳本而言,就是該腳本的路徑;
       $1,$2,....$N:腳本後面通過命令行給腳本傳遞的命令行參數;
         N>9時,引用該位置變量時需要加{},即${10}

      特殊變量:
      $@:給出的所有位置的參數的列表,當使用雙引號時,每個參數作爲單獨的字符串存在;
      $*:給出的所有位置的參數的列表,當使用雙引號時,每個參數作爲單獨的字符串存在;
      $#:表示除去$0之外,整個命令行中有多少個參數;

     read命令:
       read [-ers] [-a 數組] [-d 分隔符] [-i 緩衝區文字] [-n 讀取字符數] 
            [-N 讀取字符數] [-p 提示符] [-t 超時] [-u 文件描述符] [名稱 ...]

        -a array:定義數組(索引數組);
        -p prompt:給用戶輸出提示信息;
        -t timeout:用戶輸入的超時時間;
         name :變量或數組的名稱;如果省略此內容,bash會將read讀到的信息直接保存到內置的名爲REPLAY變量中;

     注意:
        Linux思想之一:儘量不與用戶交互;

        在使用read命令時,通常會使用-t選項來指定與用戶的交互時間,一旦超過預定時間,腳本中後續的命令內容
        會自動被執行;因此,通常需要在後面判斷通過read賦值的變量值是否爲空,如果爲空,可能爲該變量提供默認值;

        read -t 5 VAR1
          [ -z $VAR1 ] && VAR1=value1

   管理用戶腳本:
   腳本可以接受兩個參數,第一個參數爲-a或-d,第二個參數爲用戶名;如果第一個參數是-a,則創建其後面參數命名的用戶;如果第一個參數爲-d,則刪除其後面參       數命名的用戶;

     #!/bin/bash
     #
    if [ $# -ne 2 ] ; then
    echo "Make sure provide TWO arguments."
      exit 5
      fi

    if [ $1 == '-a' ] ; then
      if ! id $2 &> /dev/null ; then
        useradd $2 &> /dev/null
        echo $2 | passwd --stdin $2 &> /dev/null
        echo "User $2 created succesfully and password changed to it's username."
      else
        echo "$2 exists already."
      fi
    elif [ $1 == '-d' ] ; then
      if id $2 &> /dev/null ; then
        userdel -r $2 &> /dev/null
        echo "User $2 delete finished."
      else
        echo "$2 does not exist yet."
      fi
      else
        echo "Usage: $(basename $0) -a USERNAME | -d USERNAME"
      exit 6
    fi

改進版:使用read命令;
#!/bin/bash
#
if [ $# -ne 1 ] ; then
echo "Make sure provide ONE argument."
exit 5
fi

if [ $1 == '-a' ] ; then
read -p "Please input a username for creating: " USERNAME
if ! id $USERNAME &> /dev/null ; then
useradd $USERNAME &> /dev/null
echo $USERNAME | passwd --stdin $USERNAME &> /dev/null
echo "User $USERNAME created succesfully and password changed to it's username."
else
echo "$USERNAME exists already."
fi
elif [ $1 == '-d' ] ; then
read -p "Please input a username for deleting: " USERNAME
read -t 5 -p "confirm? Input 'yes' to continue: " CHOICE
[ -z $CHOICE ] && CHOICE='no'

if [ $CHOICE == 'yes' ] ; then
if id $USERNAME &> /dev/null ; then
userdel -r $USERNAME &> /dev/null
echo "User $USERNAME delete finished."
else
echo "$USERNAME does not exist yet."
fi
else
echo
echo "$USERNAME is not deleted."
fi
else
echo "Usage: $(basename $0) { -a | -d }"
exit 6
fi

寫腳本解決問題:
1.判斷用戶通過命令行給的一個參數是否爲整數。

#!/bin/bash

if [ $# -ne 1 ] ; then
echo "Make sure provide ONE digit."
exit 5
fi

if [[ $1 =~ ^[[:digit:]]+$ ]] ; then
echo "$1 is a pure digit."
else
echo "$1 is not a digit."
fi

    循環執行結構:
       循環:將某一段代碼或命令重複執行0,1或多次;

       一個好的循環結構,必須要包括兩個重要的環節:
        1.進入循環的條件:
          在符合要求或滿足條件時纔開始循環;

        2.退出循環條件:
          達到某個要求或符號某個條件時需要結束或終止循環的執行;

    for循環:
       1.遍歷列表的循環:
         for 名稱 [in 詞語 ... ] ; do 命令; done
         爲列表中的每個成員執行命令。

         建議在腳本中的書寫格式:
         for VAR_NAME in LIST ; do
           循環體
         done
         或
         for VAR_NAME in LIST 
         do
            循環體
         done

         注意:
         VAR_NAME:任意指定的變量名稱,變量的值是從LIST中遍歷獲取的各個元素;
         LIST:for循環需要遍歷的列表,可以通過如下方式生成列表:
            1.直接給出列表;
            2.純整數列表:
              1):花括號展開:
                   {FIRSTNUM..LASTNUM}

              2):seq命令
                    seq [OPTION]... LAST  seq 10

                    seq [OPTION]... FIRST LAST  seq 2 6

                    seq [OPTION]... FIRST INCREMENT LAST   seq 0 2 20

           3.花括號展開
                   {FIRST..LAST}

           4.命令的執行結果:
             ls /etc

           5.GLOBBING通配符

           6.某些特殊變量的值:
             $*,$@

          循環體:

            一般來說,循環體中應該包括能夠用到VAR_NAME變量的值的命令或命令的組合;如果循環體中的命令沒有用到
            VAR_NAME變量的值的話,列表的元素的個數就是此次for循環的次數;

            #!/bin/bash
            for i in {1..50} ; do 
            sum=$[sum+i]
            done
            echo "$sum" 

       例子:給腳本傳遞三個整數,要求:
1) 如果用戶傳遞過來的不是三個參數,報告正確用法;
2) 如果三個參數中有非純數字字符串,報告錯誤並提示用戶輸入數字;
3) 從三個整數中的選出最大數和最小數,並顯示出來;
4) 不能使用sort命令等排序;
[root@localhost ~]# vim a.sh
#!/bin/bash
#
if [ $# -ne 3 ] ; then
   echo " please provide three argument"
   exit 5
fi
if [[ $1 =~ [^[:digit:]] ]]||[[ $2 =~ [^[:digit:]] ]] ||[[ $3 =~ [^[:digit:]] ]] ; then
   echo "exist error"
fi
if [ $1 -ge $2 ] ; then
    if [ $1 -ge $3 ] ; then
       echo "the largest is $1"
    fi
fi
if [ $2 -ge $1 ] ; then
    if [ $1 -ge $3 ] ; then
      echo "the largest is $2"
    fi
fi
if [ $3 -ge $1 ] ; then
   if [ $3 -ge $2 ] ; then
     echo "the largest is $3"
   fi
fi
if [ $1 -le $2 ] ; then
   if [ $1 -le $3 ] ; then
     echo "the smallest is $1"
   fi
fi
if [ $2 -le $1 ] ; then
   if [ $2 -le $3 ] ; then
     echo "the smallest is $3"
   fi
fi
if [ $3 -le $1 ] ; then
  if [ $3 -le $2 ] ; then
    echo "the largest is $3"
  fi
fi

結果:
 [root@localhost ~]# bash a.sh  12  13 14
 the largest is 14
 the smallest is 12

例:給腳本傳遞一個整數,分別計算該整數以內所有偶數之和以及奇數之和。
[root@localhost ~]# vim q.sh
#!/bin/bash
declare i sum=0 sum1
if [[ $1 =~ [^[:digit:]] ]] ;then
echo "$1should be an integer"
exit 5
fi
if [ "$1" -eq 1 ] ; then
echo "奇數和爲1"
exit 5
fi
if [ "$1" -eq 2 ] ; then
echo "偶數和爲2"
exit 5
fi

for i in $(seq 1 2 $1) ; do
sum=$[sum+i]

done
echo "奇數和爲$sum"
for i in $(seq 2  2 $1 ) ; do
sum1=$[sum1+i]

done
echo "偶數的和爲$sum1"

結果:[root@localhost ~]# bash q.sh 3
奇數和爲4
偶數的和爲2
[root@localhost ~]# bash q.sh 5
奇數和爲9
偶數的和爲6

        例:      
8.利用RANDOM變量隨機生成十個數字,顯示出這十個數字,並顯示出其中的最大值和最小值。

[root@localhost ~]# vim 000

#!/bin/bash

touch ytc
for i in $( seq 1 10 ) ; do
echo $RANDOM &>> ytc
done
sort -n ytc| head -1
sort -n ytc| tail -1
rm -r ytc

結果:
[root@localhost ~]# bash 000
7119
28639
[root@localhost ~]# bash 000
412
31953

 例:給腳本傳遞一個數字作爲行總數,分別打印由*組成的最小銳角朝上和朝下的等腰三角形以及菱形。
  角朝下 

[root@localhost ~]# vim 9
#!/bin/bash

if [ $# -ne 1 ] ; then
echo " usage: $(basename $0) integer"
exit 5
fi

if [[ $1 =~ [^[:digit:]] ]] ; then
echo " usage: $(basename $0) integer"
exit 5
fi

linenum=$1
for i in $(seq $linenum) ; do
for j in $(seq $[i-1 ]); do
echo -n " "
done
for k in $(seq $[2(linenum-i)+1 ]) ; do
echo -n "
"
done
echo
done

結果:[root@localhost ~]# bash 9 8





*******
 *****
  ***
   *

角朝上:
[root@localhost ~]# vim 10
#!/bin/bash

if [ $# -ne 1 ] ; then
echo "Usage: $(basename $0) INTEGER"
exit 5
fi

if [[ $1 =~ [^[:digit:]] ]] ; then
echo "Usage: $(basename $0) INTEGER"
exit 6
fi

linenum=$1
for i in $( seq $linenum) ; do
for j in $( seq $[linenum-i]) ; do
echo -n " "
done
for k in $(seq $[2i-1]) ; do
echo -n "
"
done
echo
done

結果:
[root@localhost ~]# bash 10 8
*


 *****
*******




菱形:
[root@localhost ~]# vim 11
#!/bin/bash

if [ $# -ne 2 ] ; then
echo "Usage: $(basename $0) INTEGER"
exit 5
fi

if [[ $1 =~ [^[:digit:]] ]] ; then
echo "Usage: $(basename $0) INTEGER"
exit 6
fi

linenum=$1
for i in $( seq $linenum) ; do
for j in $( seq $[linenum-i]) ; do
echo -n " "
done
for k in $(seq $[2i-1]) ; do
echo -n "
"
done
echo
done

linenum=$2

for i in $(seq $linenum) ; do
for j in $(seq $[i ]); do
echo -n " "
done
for k in $(seq $[2(linenum-i)+1 ]) ; do
echo -n "
"
done
echo
done

結果:
[root@localhost ~]# bash 11 8 7
*


 *****
*******







*******
 *****
  ***
   *

例:分別打印順序和旋轉的九九乘法表。
1)順序的九九乘法表是正常的九九乘法表;
[root@localhost ~]# vim 8
#!/bin/bash

  for i in {1..9} ; do
     for j in $(seq $i) ; do
     echo -ne "$i×$j=$[i*j]\t"
     done
     echo
  done

   2)旋轉的九九乘法表是第一行是1×1=1 1×2=2 1×3=3 1×4=4 ... 1×9=9; 第二行是2×2=4 2×3=6 2×4=8 ... 2×9=18; ... 第九行是9×9=81;
 [root@localhost ~]# vim 7
  #!/bin/bash

  for i in {1..9} ; do
     for j in $(seq $i 9) ; do
     echo -ne "$i×$j=$[i*j]\t"
     done
     echo
  done

  結果:
  [root@localhost ~]# bash 7

1×1=1 1×2=2 1×3=3 1×4=4 1×5=5 1×6=6 1×7=7 1×8=8 1×9=9
2×2=4 2×3=6 2×4=8 2×5=10 2×6=12 2×7=14 2×8=16 2×9=18
3×3=9 3×4=12 3×5=15 3×6=18 3×7=21 3×8=24 3×9=27
4×4=16 4×5=20 4×6=24 4×7=28 4×8=32 4×9=36
5×5=25 5×6=30 5×7=35 5×8=40 5×9=45
6×6=36 6×7=42 6×8=48 6×9=54
7×7=49 7×8=56 7×9=63
8×8=64 8×9=72
9×9=81

     總結:
         1.進入循環的條件:LIST中還有未被取盡的元素;
         2.退出循環的條件:LIST中的元素被取盡;
         3.for循環幾乎不會出現死循環;
         4.在執行循環的過程中,需要將整個LIST載入內存,因此對於大列表來說,可能會消耗較多的內存及CPU資源;

編程思想:
將人類的自然語言轉換成程序的代碼語言的方式;

DevOPS

不會開發的運維是沒有出路的。

流程圖

如果 a大於3, 那麼
創建一個用戶
fi

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