文章目錄
前置參考文章
開篇
如果讀者看過了前三篇文章很容易發現,在我們使用命令的時候(不管是系統篇,權限篇),都是以單個命令執行程序的,而這篇是以多個命令組合、命令前後關聯的角度來看待問題的。因此要牢牢抓住如下特性 異常,重定向,管道
。才能做到遊刃有餘。
在shell編程中,第一個特性,可能大家都沒有注意到,shell編程中存在異常捕獲的概念嗎?有,當然有,但是與一般高級語言的異常處理不太一樣。
前置命令和後置命令
本人特意指出這兩個概念有一個核心目的,就是方便本文後面的概念的描述。否則太多的“前面的命令”,“後面的命令”,這樣的字眼出現,顯得有些笨拙冗餘。
在複合命令中,在複合命令操作符(&&,||,<,>,<<,>>) 左端(前面)的命令叫做前置命令,後置命令就是複合命令操作符右端(後面)的命令。
異常(非零狀態碼)
狀態碼
首先要了解異常,必須要瞭解狀態碼,即命令最終的狀態信息,一般正常的話爲0
表11-2 Linux退出狀態碼
狀 態 碼 描 述
0 命令成功結束
1 一般性未知錯誤
2 不適合的shell命令
126 命令不可執行
127 沒找到命令
128 無效的退出參數
128+x 與Linux信號x相關的嚴重錯誤
130 通過Ctrl+C終止的命令
255 正常範圍之外的退出狀態碼
在腳本語言中,異常定義的公式如下
$ exit num ; # 其中 (num!=0)
即在腳本中任一段命令中拋出的狀態碼num 不等於 0 ,表示了拋出了異常,而不同於一般程序語言中的異常
特點(與高級語言對比)
- 不能捕獲,也就是沒有try…catch這樣的語法,
- 一旦出現異常(exit num) 程序依然能夠正確執行出錯命令之後的命令
如果指定命令異常之後不想執行之後的邏輯命令,請使用特殊變量 ? 命令所處位置的最近一次邏輯可執行命令執行之後的狀態碼,默認情況下,正常執行完成都會拋出0(代表正常退出)
#!/bin/bash
ldd; // ldd: missing file arguments
if [ $? -eq 0 ];then
echo "正常退出";
else
echo "異常退出"; // 執行該分支
exit 2; // 退出之後結束代碼
fi
echo "asads" // 不執行
該代碼中 ldd沒有指定的命令,所以會拋出一個異常,因此會走分支2。在這裏順便提一句,在/bin/bash登陸shell的模式下,支持整數運算,不支持浮點值運算,這是一個非常巨大的缺陷,因此爲了支持浮點運算,可以使用另一個非常高級的shell (zsh shell 佐羅/瑞士軍刀).
在 zsh 中 擴展了bash shell 的很多功能,其最大的特性就是提供模塊加載的功能,以內建命令的執行速度,執行自己擴展的功能。有興趣的同學可以根據自己需要安裝 zsh ubuntu zsh 官網
使用複合命令連接符(邏輯判斷符)(&& 或則 ||)
可以使用 && ,使得在前置命令成功執行的情況下可以繼續執行 && 的後置命令
原理也是簡單,就是在判斷 && 前置命令執行退出狀態碼爲0(正常退出)的情況下能夠繼續執行後置命令。
可以使用 || ,作用於前置命令退出狀態碼非零的情況下,能夠執行後置命令,當然默認shell執行環境下,不管前置命令是否正常退出,都會執行後置命令,但是 || 只有前後命令失敗的情況下,才能執行後置命令
#!/bin/bash
ldd && echo "成功執行"
ls || echo "ls執行失敗"
都會不執行後面的echo 命令
輸入輸出重定向符
< > << >>
-
> 覆蓋寫入,++輸出重定向++
-
>> 追加寫入 ++內聯輸出重定向++
-
< 回寫,++輸入重定向++
-
<< EOF(前置字符串可以爲任一字符串) … EOF(必須更前置字符串一致) ++內聯輸入重定向++
-
>& 臨時重定向 代表這前置輸出被臨時緩存到後置輸出中,效果類似於>,不同的是,會清空前置輸出,後置最後導流到後置輸出,這個更像一個 ++管道++。也有人叫做 合併輸出重定向
-
exec 永久重定向
a) 輸出重定向 與 內聯輸出重定向的區別
homewell:~/shell1$ echo 1 > tail.logs
homewell:~/shell1$ cat tail.logs
1
homewell:~/shell1$ echo 2 > tail.logs
homewell:~/shell1$ cat tail.logs
2
homewell:~/shell1$ echo 3 >> tail.logs
homewell:~/shell1$ cat tail.logs
2
3
b) ++輸入重定向++ 與 ++內聯輸入重定向++
其實,沒有必要記住哪個符號是輸入,哪個符號是輸出,而是隻要理解箭頭地方向就是數據流動的方向,那麼時間久了,也會自然而然想給這個方向做個定義,那麼就會很自然地我們會 ++以前置命令作爲主軸++,只要 ++流入++ 前置命令地方向就叫做 ++輸入重定向++,從前置命令 ++流出++ 並向後置命令傳入地就叫做 ++輸出重定向++
程序接收輸入
擴展 標準輸入(0),標準輸出(1),標準錯誤(2)
記憶技巧(性)把女人比作0,男人比作1,這樣輸入和輸出的方向就很容易記了
在實際工作中,數據的輸入和輸出往往是經常使用的,比如日誌輸入到文件中,或者輸入到控制檯上。或者更高級一些,我們想把錯誤日誌輸出到控制檯,而正常的日誌輸出到文件中(數據的分流,是不是很像過濾操作,亦或是導流)
文件描述符 | 縮寫 | 描述 |
---|---|---|
0 | STDIN | 標準輸入 |
1 | STDOUT | 標準輸出 |
2 | STDERR | 標準錯誤 |
在linux系統中,內核把所有實例或者對象當作文件來處理
奇怪的現象
homewell:~/shell1$ ls -al badfile > test3
ls: cannot access 'badfile': No such file or directory
homewell:~/shell1$ cat test3
homewell:~/shell1$
錯誤信息只輸出到了控制終端顯示,並沒有輸出到文件中,這是一個很讓人疑惑的地方。其實shell對錯誤信息的處理與普通輸出是分開處理的。默認情況下也就是2號文件描述符與1號文件描述符默認指向的位置是一樣的,都是發送到控制終端界面上的。然而2號描述符並不會隨着重定向符(>)的方向而改變。
改變STDERR重定向到文件 (2>)
2> 字面含義,把2號文件描述符(標準錯誤信息)輸出到後置命令中(文件名稱。可以不存在)
homewell:~/shell1$ ls -al badfile 2> test3
homewell:~/shell1$ cat test3
ls: cannot access 'badfile': No such file or directory
同時把標準輸出(1)與標準錯誤(2)導向同一個文件
1> 代表1號文件描述符所處的默認位置(控制終端)重新導向(覆蓋)新地址
1>> 代表1號文件描述符所處的默認位置(控制終端)重新導向(追加)新地址
homewell:~/shell1$ ls -al test badtest test2 2> test5 1>> test5
homewell:~/shell1$ cat test5
ls: cannot access 'test': No such file or directory
ls: cannot access 'badtest': No such file or directory
test2:
total 8
drwxrwx--- 2 homewell homewell 4096 10月 9 09:35 .
drwxrwx--- 4 homewell homewell 4096 10月 9 09:38 ..
簡寫
(&> test6) == (2> test6 1>> test6)
& 可以理解爲 所有輸出符的合併(1+2+。。。)
homewell:~/shell1$ ls -al test badtest test2 &> test6
homewell:~/shell1$ cat test6
ls: cannot access 'test': No such file or directory
ls: cannot access 'badtest': No such file or directory
test2:
total 8
drwxrwx--- 2 homewell homewell 4096 10月 9 09:35 .
drwxrwx--- 4 homewell homewell 4096 10月 9 09:38 ..
c) 臨時重定向 (>&)
這裏>& 更像一個管道 把 &的前置輸出與後置輸出合併爲後置輸出
本人更習慣 1 (空格) >& (空格)2 這樣,更能體會臨時重定向作爲操作符來使用,而不是把>&2 作爲一個整體來記憶,這樣我們就能活學活用,看如下事例
homewell:~/shell1$ cat error.sh
#!/bin/bash
echo "This is an error message" 1 >& 2
echo "This is nomal message"
homewell:~/shell1$ ./error.sh 2 > error #只把錯誤輸出導入到error中
This is an error message 1
homewell:~/shell1$ cat error
This is nomal message
homewell:~/shell1$ ./error.sh 2 > error >& error2 # 把錯誤輸出與標準輸出都合併到error2中
homewell:~/shell1$ cat error
homewell:~/shell1$ cat error2
This is an error message 1
This is nomal message
d) 永久重定向 (exec)
永久的意思就是方向一旦確定,就無法再回頭了,這個是比較麻煩的地方,也是頭痛的地方
永久改變輸出
homewell:~/shell1$ cat -n test11
1 #!/bin/bash
2 exec 2> test11err
3
4 echo "還沒有執行exec永久重定向"
5
6 exec 1> test11out
7
8 echo "執行永久重定向並標準輸出到test11out文件"
9
10 echo "執行永久重定向並標準錯誤到testerr中" >& 2
homewell:~/shell1$ ./test11 &> abc
homewell:~/shell1$ cat abc
還沒有執行exec永久重定向
homewell:~/shell1$ cat test11err
執行永久重定向並標準錯誤到testerr中
homewell:~/shell1$ cat test11out
執行永久重定向並標準輸出到test11out文件
永久改變輸入
exec 0< testfile 代表在模塊中接下來的程序默認輸入永久改變爲從testfile文件作爲輸入(這裏暫時不講while的語法,見後期的結構化命令篇)
homewell:~/shell1/linuxlearnging$ cat testfile
a
b
c
homewell:~/shell1/linuxlearnging$ cat input3.sh
#!/bin/bash
exec 0< testfile
while read line
do
echo "your content:" $line
done
homewell:~/shell1/linuxlearnging$ ./input3.sh
your content: a
your content: b
your content: c
exec 擴展
exec 不僅可以當永久重定向使用,也可以作爲類似於(./ source)
作爲執行者(executor),執行指定的命令和參數來替換當前shell進程
exec 命令行 參數
不管執行成不成功都不會繼續往後執行,也就是直接替換了當前的shell進程的所有內容,並直接跳出(這個是最猛的宏替換)
homewell:~/shell1/linuxlearnging$ cat -n ./exectest.sh
1 #!/bin/bash
2
3 var1=zhangll
4 echo "$$"
5 echo "$BASHPID"
6 exec 0< testfile
7
8 echo "exec1 after var1: $var1"
9 echo "$BASHPID"
10
11
12 exec echo "exec $BASHPID"
13 echo "exec2 after var1: $var1"
homewell:~/shell1/linuxlearnging$ ./exectest.sh
14347
14347
exec1 after var1: zhangll
14347
exec 14347
homewell:~/shell1/linuxlearnging$ echo "$?"
0
管道
管道的理解是一個十分頭疼的問題,本人剛剛接觸的時候也只是以比較膚淺的觀點去理解管道,普遍都有個共識,前一截管道(前置命令)的輸出結果可以作爲後一截命令(後置命令)的輸入數據,然而往往忽略了一點,也就是這個過程是實時的,並不是說前置命令執行完之後纔會執行後置命令,重點在於 前置命令一旦產生輸出(往往前置命令的輸出與代碼結束式共存的,所以很難把握) 之後,系統內部會立馬產生輸出到後置命令作爲輸入。
理解這點我們需要一個工具輔助
當我們以tail命令作爲輸出的時候,tail命令是一直不停循環的,執行,根本沒有停歇過。然後我們用| 命令卻很好地把前置命令地輸出作爲後置命令地輸出。
比如 只過濾一個擁有a地輸出
管道符 |
# tail -f tail.logs | grep a
因此,當你在使用管道的時候,切記她是 ++流式的++
參考資料
《Linux命令行與shell腳本編程大全》
未完待續。。。