Linux shell 整理之 語法結構篇(五)


前置參考文章

Linux shell 整理之 基本概念篇(一)

Linux shell 整理之 基本概念篇(二)

Linux shell 整理之 用戶權限篇(三)

Linux shell 整理之 複合命令行篇(四)

背景

結構化語句指的是shell編程允許改變shell的正常的執行流.比如 條件分支語句(if case),循環語句(for while until).其實更多的個高級語言,都是有這些語法特性的,學過一般的程序語言,就能夠很方便地理解這塊內容.

條件分支語句

if條件分支

if then 最基本的語句

格式1

zhangll 23:15:20 linuxshell$ cat ifthen2.sh 
    #!/bin/bash
    
    if pwd;then
            echo "zhangll"
    fi
zhangll 23:15:30 linuxshell$ ./ifthen2.sh 
    /home/zhangll/linuxshell
    zhangll

格式2

zhangll 23:17:11 linuxshell$ cat ifthen.sh 
    #!/bin/bash
    
    if pwd
    then
            echo "zhangll"
    fi
zhangll 23:17:17 linuxshell$ ./ifthen.sh 
    /home/zhangll/linuxshell
    zhangll

在一般的編程語言的條件判斷語句中if 後面跟的是邏輯條件判斷,但是在shell中並不是這樣,而是在if之後跟着是命令執行之後的退出狀態碼,如果退出碼是0,則執行then後面的語句,否則進入其他分支

if then else

zhangll 23:22:25 linuxshell$ cat ifthenelse.sh 
    #!/bin/bash
    
    if ls nofile 
    then
            echo "has file"
    else
            echo "no file"
    fi
zhangll 23:22:31 linuxshell$ ./ifthenelse.sh 
    ls: 無法訪問'nofile': 沒有那個文件或目錄
    no file

可以發現ls nofile會輸出錯誤信息,因此不會走then,而是最走 else 分支

if then elif then else

當有條件的時候需要使用elif then來做更多的判斷

zhangll 23:27:48 linuxshell$ cat ifthenelifelse.sh 
#!/bin/bash

    if ls nofile 
    then
            echo "has file"
    elif ls .
    then
            echo "has ."
    else
            echo "no file"
    fi
zhangll 23:28:05 linuxshell$ ./ifthenelifelse.sh 
    ls: 無法訪問'nofile': 沒有那個文件或目錄
    abc         ee          ifthenelifelse.sh  pid2.sh     testing   test.log
    abc.txt     iftest.sh   ifthenelse.sh      pid.sh      testing]  var2
    compare.sh  ifthen2.sh  ifthen.sh          sorted.txt  Testing]  var2]
    has .

當你覺得錯誤信息打印太多太亂,你可以使用exec永久重定向輸出,這裏可以查看之前的文章

case

case一般與模式匹配相關

zhangll 23:38:55 linuxshell$ cat case.sh 
    #/bin/bash
    
    case $USER in
    zhanll | zhangl)
            echo "hello z";;
    (zhang*l) #這裏特意指出來()也可以作爲匹配條件使用
            echo "hello zll";;
    *)
            echo "hello default";;
    esac
zhangll 23:38:59 linuxshell$ ./case.sh 
    hello zll

test處理特殊條件

在shell中提供了test方法,用來判斷條件是否滿足
一旦滿足就會退出返回0退出碼,否則返回非0退出碼

這樣就很容易使用邏輯比較做測試

使用[]來代替test函數

也即是說在使用[condition]的時候等價與test condition

test支持如下三種條件測試

1)數值判斷

相等(equal)不相等(not equal)判斷

-eq
-ne

大於(greater then)與小於(less then)

-gt
-lt

大於等於(greater equal)與 小於等於(less equal)

-ge
-le

zhangll 23:57:07 linuxshell$ cat equal.sh 
    #!/bin/bash
    
    var1=10
    var2=20
    if test $var1 -gt $var2;then
    #if [ $var1 -gt $var2 ];then
            echo "$var1 is greater then $var2"
    else
            echo "$var1 is less then $var2"
    fi
zhangll 23:57:15 linuxshell$ ./equal.sh 
    10 is less then 20

2)字符串判斷個

只介紹邏輯判斷符,判斷邏輯同哦你

邏輯判斷 描述
str1 = str2 檢查 str1 是否和 str2 相同
str1 != str2 檢查 str1 是否和 str2 不同
str1 < str2 檢查 str1 是否比 str2 小
str1 > str2 檢查 str1 是否比 str2 大
-n str1 檢查 str1 的長度是否非0
-z str1 檢查 str1 的長度是否爲0

3)文件判斷

邏輯判斷 描述
-d file 檢查 file 是否存在並是一個目錄
-e file 檢查 file 是否存在
-f file 檢查 file 是否存在並是一個文件
-r file 檢查 file 是否存在並可讀
-n str1 檢查 str1 的長度是否非0
-s file 檢查 file 是否存在並非空
-w file 檢查 file 是否存在並可執行
-x file 檢查 file 是否存在並可執行
-O file 檢查 file 是否存在並屬當前用戶所有
-G file 檢查 file 是否存在並且默認組與當前用戶相同
file1 -nt file2 檢查 file1 是否比 file2 新
file1 -ot file2 檢查 file1 是否比 file2 舊

高級判斷

複合判斷

(( expression )) 高級數值比較

雙圓括號不僅支持比較運算(> ;< ;!= ;=),但是不支持(-eq -ne -gt -lt …),同時也支持賦值操作

zhangll 22:12:17 linuxshell$ cat -n doubleI.sh 
     1  #/bin/bash
     2
     3  var1=20
     4  var2=3
     5  if (( $var1 > $var2 ));then
     6          (( var3 = $var1 + $var2 ))
     7          echo "$var1 is bigger than $var2, sum is $var3"
     8  else
     9          echo "$var1 is smaller than $var2"
    10  fi
zhangll 22:12:22 linuxshell$ ./doubleI.sh 
    20 is bigger than 3, sum is 23

[[ expression ]] 高級字符串比較

雙方括號中的表達式是針對字符串的,支持一切test支持的字符操作,但是多了一個特性.模糊匹配(==)

在shell中使用(=)來比較字符ascii值的大小,並不是一般的賦值操作.這個非常重要
因此我們用(==)來作爲模糊匹配就很合理了

zhangll 21:59:06 linuxshell$ cat -n doubleF.sh 
     1  #/bin/bash
     2
     3  if [[ $USER == z* ]];then
     4          echo "hello $USER"
     5  else
     6          echo "no"
     7  fi
zhangll 21:59:14 linuxshell$ ./doubleF.sh 
hello zhangll

擴展內容 $[ operation ]

在shell編程中 $[ operation ]其實是對expr命令 的一種替代,不過expr支持更多的操作,或者通過

    man expr

,不過前者更加受歡迎,類似於 條件語句中的[]操作替代test命令一樣

    man test

循環語句

循環結構也是非常常見的,除了分支倒流之外,還可以做閉環處理,不斷處理同一邏輯,循環結構的發明本身就是非常了不起的

for

基本語法

zhangll 22:19:04 linuxshell$ cat -n for1.sh 
     1  #!/bin/bash
     2
     3  var="a  : b : c : d"
     4 
     5  for i in $var
     6  do
     7          echo line:$i
     8  done
zhangll 22:19:07 linuxshell$ ./for1.sh 
    line:a
    line::
    line:b
    line::
    line:c
    line::
    line:d

稍微複雜一些的結構

當遍歷的數組有單引號或者特殊字符時候的處理方法

1 使用反斜槓轉意字符

2 用雙引號包裹字符 (這裏特別指出,雙引號並不佔用內存空間,只是提醒編譯器在被包裹的字符串作爲一個整體被處理)

zhangll 22:30:01 linuxshell$ cat -n for1t.sh 
     1  #!/bin/bash
     2
     3  for i in I\'m "zhangll'" , we are family
     4  do
     5          echo line:$i
     6  done
zhangll 22:32:13 linuxshell$ ./for1t.sh 
    line:I'm
    line:zhangll'
    line:,
    line:we
    line:are
    line:family

for循環注意的雙引號問題

先見如下語句

zhangll 22:53:15 linuxshell$ cat -n for2t.sh 
     1  #!/bin/bash
     2  var="I'm zhangll' :, we are family"
     3  #IFS=$'\n':;" # 因爲換行符是兩個字符所以用單引號括起來
     4  for i in $var
     5  do
     6          echo line:$i
     7  done
     8  echo "##############$i"
     9  #IFS=$IFS.OLD #恢復默認分隔符
    10  for i in "I'm zhangll' , we are family"
    11  do
    12          echo line:$i
    13  done

在第一個for循環中,我們用雙引號聲明瞭一個字符串組,其實for循環會根據默認的IFS(默認是空格或者製表符(\t)或者換行符(\n))作爲內部字段分隔符,我們可以在使用for循環之前使用IFS=\n:表示以換行和冒號作爲分隔符,可以自己測試一下

在第二個for循環我們直接使用雙引號使得"I’m zhangll’ , we are family"做爲一個整體使用

結果

zhangll 22:40:01 linuxshell$ ./for2t.sh 
line:I'm
line:zhangll'
line:,
line:we
line:are
line:family
##############family
line:I'm zhangll' , we are family

這裏還會發現,for中的循環變量可以在for循環外部使用,這個非常特別

練習題目

for i in "./*"
do
        echo file name : $i
done

這個結果會是怎麼樣的呢?

for循環與重定向的結合

上一章中我們瞭解了重定向相關的知識,現在有個需求,我們只想把for循環內部打印到指定文件夾中如何處理?

語法 done >filename 等價 done 1>filename

默認1爲輸出重定向,還記得男人的比喻嗎?

zhangll 23:09:14 linuxshell$ cat -n for4t.sh
     1  #!/bin/bash
     2  for (( i = 0; i < 10; i++ ))
     3  do
     4          echo $i
     5  #done > $0.log
     6  done 1> $0.log
     7  echo "外部使用到$i"
zhangll 23:09:20 linuxshell$ ./for4t.sh 
外部使用到10
zhangll 23:09:26 linuxshell$ cat for4t.sh.log 
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

while

while語句相當於下面語句的縮寫版本

    #僞代碼,但不妨礙理解
 for (;if test command;);
 do
   do something....
 done

縮寫爲

    while test comman
    do
    do something
    done

這就是對while最精髓地解.當一個條件不滿足的時候才退出內部的代碼

特別留意

#!/bin/bash
#for(;test 1;);
count=10
count2=110
while echo $count $count2
        [ $count2 -ge 0 ]
        [ $count -ge 0 ]
do
     count=$[$count - 1]
     count2=$[$count2-1]
     echo "count : $count"
        #sleep(1)
done;

while支持多條件只有在最後一個語句退出碼非0的時候才退出(每個test語句佔一行),因此要特別小心最後一個語句退出碼永遠都爲0的情況

until

他是while的反面 ,語法與while一致

循環控制

關鍵詞 break continue

在循環控制中,break和continue默認分別控制者打斷最近一層循環繼續最近一層循環的作用

    (base) zhangll 22:29:40 linuxshell$ cat -n ./while2.sh 
     1  #!/bin/bash
     2  #for(;test 1;);
     3  count=3
     4  count2=110
     5  while echo $count $count2
     6          [ $count -ge 0 ]
     7  do   
     8          while [ $count2 -ge 0 ]
     9          do
    10                  count2=$[$count2-1]
    11                  echo " 內層循環count2: $count2"
    12                  if (($count2 >= 99 ))
    13                  then
    14                          break
    15                  fi;
    16          done;
    17       count=$[$count - 1]
    18       echo "外層循環count : $count"
    19          #sleep(1)
    20  done;

(base) zhangll 22:29:49 linuxshell$ ./while2.sh 
	    3 110
	  			   內層循環count2: 109
			    外層循環count : 2
	    2 109
	 		 	   內層循環count2: 108
			    外層循環count : 1
	    1 108
	   			  內層循環count2: 107
			    外層循環count : 0
	    0 107
	 		  	  內層循環count2: 106
	  		  外層循環count : -1
	    -1 106

爲什麼說是默認呢?因爲break關鍵詞默認會被當作

 break 1

業就是代表最近一層循環,當然如果值爲 break 2,代表退出最近的第二層循環

continue語法與break 一致

高級應用

user.csv文件中每行對應姓名與年齡(以逗號分割),現在需要分割文件中的字符串,並輸出所有年齡大於20歲的人,並以新的格式輸出到outer文件中,此時使用了重定向,while 多test命

(base) zhangll 22:59:07 linuxshell$ cat user.csv 
        zhangll01,20
        jianglina02,30
        zhutou,39
(base) zhangll 22:59:09 linuxshell$ cat -n ./whileHight.sh 
     1  #!/bin/bash
     2  inputfile="user.csv"
     3  outputfile="outer"
     4  while IFS=','
     5          read -r name age
     6  do  
     7          if (( $age > 20 ))
     8          then
     9                  echo "name : $name,age: $age "
    10          fi
    11  done < $inputfile > $outputfile
(base) zhangll 22:59:15 linuxshell$ ./whileHight.sh 

(base) zhangll 22:59:23 linuxshell$ cat outer 
        name : jianglina02,age: 30 
        name : zhutou,age: 39  39 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章