# 第六章 退出與退出狀態
> Bourne shell里存在不明確之處,但人們也會使用它們。
>
> —— Chat Ramey
跟C程序類似,`exit` 命令被用來結束腳本。同時,它也會返回一個值,返回值可以被交給父進程。
每個命令都會返回一個退出狀態(exit status),有時也叫做返回狀態(return status)或退出碼(exit code)。命令執行成功返回0,如果返回一個非0值,通常情況下會被認為是一個錯誤代碼。一個運行狀態良好的UNIX命令、程序和工具在正常執行退出后都會返回一個0的退出碼,當然也有例外。
同樣地,腳本中的函數和腳本本身也會返回一個退出狀態。在腳本或者腳本函數中執行的最后的命令會決定它們的退出狀態。在腳本中,`exit nnn` 命令將會把nnn退出狀態碼傳遞給shell(nnn 必須是 0-255 之間的整型數)。
>  當一個腳本以不帶參數的 `exit` 來結束時,腳本的退出狀態由腳本最后執行命令決定(`exit` 命令之前)。
```bash
#!/bin/bash
COMMAND_1
...
COMMAND_LAST
# 將以最后的命令來決定退出狀態
exit
```
> `exit`,`exit $?` 以及省略 `exit` 效果等同。
```bash
#!/bin/bash
COMMAND_1
...
COMMAND_LAST
#將以最后的命令來決定退出狀態
exit $?
```
```bash
#!/bin/bash
COMMAND_1
...
COMMAND_LAST
#將以最后的命令來決定退出狀態
```
`$?` 讀取上一個執行命令的退出狀態。在一個函數返回后,`$?` 給出函數最后執行的那條命令的退出狀態。這就是Bash函數的"返回值"。[^1]
在[管道](http://tldp.org/LDP/abs/html/special-chars.html#PIPEREF)執行后,`$?` 給出最后執行的那條命令的退出狀態。
在腳本終止后,命令行下鍵入`$?`會給出腳本的退出狀態,即在腳本中最后一條命令執行后的退出狀態。一般情況下,0為成功,1-255為失敗。
樣例 6-1. 退出與退出狀態
```bash
#!/bin/bash
echo hello
echo $? # 返回值為0,因為執行成功。
lskdf # 不認識的命令。
echo $? # 返回非0值,因為失敗了。
echo
exit 113 # 將返回113給shell
# 為了驗證這些,在腳本結束的地方使用“echo $?”
# 按照慣例,'exit 0' 意味著執行成功,
#+ 非0意味著錯誤或者異常情況。
# 查看附錄章節“退出碼的特殊含義”
```
`$?` 對于測試腳本中的命令的執行結果特別有用(查看樣例 16-35和樣例 16-20)。
>  邏輯非操作符 [!](http://tldp.org/LDP/abs/html/special-chars.html#NOTREF) 將會反轉測試或命令的結果,并且這將會影響退出狀態。
樣例 6-2. 否定一個條件使用!
```bash
true # true 是 shell 內建命令。
echo "exit status of \"true\" = $?" # 0
! true
echo "exit status of \"! true\" = $?" # 1
# 注意在命令之間的 "!" 需要一個空格。
# !true 將導致一個"command not found"錯誤。
#
# 如果一個命令以'!'開頭,那么將調用 Bash 的歷史機制,顯示這個命令被使用的歷史。
true
!true
# 這次就沒有錯誤了,但是同樣也沒有反轉。
# 它不過是重復之前的命令(true)。
# ============================================================ #
# 在 _pipe_ 前使用 ! 將改變返回的退出狀態。
ls | bogus_command #bash: bogus_command: command not found
echo $? #127
>
! ls | bogus_command #bash: bogus_command:command not found
echo $? #0
# 注意 ! 不會改變管道的執行。
# 只改變退出狀態。
#============================================================ #
>
# 感謝 Stéphane Chazelas 和 Kristopher Newsome。
```
>  某些特定的退出碼具有一些特定的[保留含義](http://tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF),用戶不應該在自己的腳本中重新定義它們。
[^1]: 在函數沒有用[return](http://tldp.org/LDP/abs/html/complexfunct.html#RETURNREF)來結束這個函數的情況下。
- 第一部分 初見shell
- 1. 為什么使用shell編程
- 2. 和Sha-Bang(#!)一起出發
- 2.1 調用一個腳本
- 2.2 牛刀小試
- 第二部分 shell基礎
- 3. 特殊字符
- 4. 變量與參數
- 4.1 變量替換
- 4.2 變量賦值
- 4.3 Bash弱類型變量
- 4.4 特殊變量類型
- 5. 引用
- 5.1 引用變量
- 5.2 轉義
- 6. 退出與退出狀態
- 7. 測試
- 7.1 測試結構
- 7.2 文件測試操作
- 7.3 其他比較操作
- 7.4 嵌套 if/then 條件測試
- 7.5 牛刀小試
- 8. 運算符相關話題
- 8.1 運算符
- 8.2 數字常量
- 8.3 雙圓括號結構
- 8.4 運算符優先級
- 第三部分 shell進階
- 10. 變量處理
- 10.1 字符串處理
- 10.1.1 使用 awk 處理字符串
- 10.1.2 參考資料
- 10.2 參數替換
- 11. 循環與分支
- 11.1 循環
- 11.2 嵌套循環
- 11.3 循環控制
- 11.4 測試與分支
- 12. 命令替換
- 13. 算術擴展
- 14. 休息時間
- 第五部分 進階話題
- 19. 嵌入文檔
- 20. I/O 重定向
- 20.1 使用 exec
- 20.2 重定向代碼塊
- 20.3 應用程序
- 22. 限制模式的Shell
- 23. 進程替換
- 26. 列表結構
- 25. 別名