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


前置參考文章

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

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

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

開篇

如果讀者看過了前三篇文章很容易發現,在我們使用命令的時候(不管是系統篇,權限篇),都是以單個命令執行程序的,而這篇是以多個命令組合、命令前後關聯的角度來看待問題的。因此要牢牢抓住如下特性 異常重定向管道
。才能做到遊刃有餘。

在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 命令

輸入輸出重定向符

< > << >>

  1. > 覆蓋寫入,++輸出重定向++

  2. >> 追加寫入 ++內聯輸出重定向++

  3. < 回寫,++輸入重定向++

  4. << EOF(前置字符串可以爲任一字符串) … EOF(必須更前置字符串一致) ++內聯輸入重定向++

  5. >& 臨時重定向 代表這前置輸出被臨時緩存到後置輸出中,效果類似於>,不同的是,會清空前置輸出,後置最後導流到後置輸出,這個更像一個 ++管道++。也有人叫做 合併輸出重定向

  6. 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腳本編程大全》

未完待續。。。

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