文章目錄
前置參考文章
背景
結構化語句指的是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