您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何在bash中退出狀態(tài)機(jī)制,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
程序的退出狀態(tài)
當(dāng)一個(gè)程序結(jié)束時(shí)會(huì)向父進(jìn)程報(bào)告自己的退出狀態(tài)( exit status ). 通過(guò)傳遞 int 類型的變量給庫(kù)函數(shù) exit 或系統(tǒng)調(diào)用 _exit 可以設(shè)置當(dāng)前程序的退出狀態(tài), 在 Linux 中, 通過(guò) WEXITSTATUS 返回的退出狀態(tài)的值域?yàn)?[0, 255] 之間的整數(shù) . 如果傳遞的值不在這個(gè)范圍內(nèi), 內(nèi)核會(huì)自動(dòng)幫你強(qiáng)轉(zhuǎn)為 u_int8_t . 通過(guò) waitpid 庫(kù)函數(shù)可以得到子進(jìn)程的退出狀態(tài), 其值存儲(chǔ)在參數(shù) wstatus 的低 8 位中.
// 定義在 wait.h 中 # define WEXITSTATUS(status) __WEXITSTATUS (status) // 定義在 waitstatus.h 中 /* If WIFEXITED(STATUS), the low-order 8 bits of the status. */ #define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
下面這個(gè)例子展示了如何使用 waitpid 及相關(guān)宏函數(shù)獲取子進(jìn)程的退出狀態(tài):
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #define PARENT_EXIT 10086 #define CHILD_EXIT -10 int main() { pid_t pid = fork(); if (pid > 0) { int wstatus; // 父進(jìn)程等待子進(jìn)程執(zhí)行完畢, 用 WUNTRACED 選項(xiàng)追蹤已結(jié)束的子進(jìn)程 pid_t child_pid = waitpid(pid, &wstatus, WUNTRACED); if (WIFEXITED(wstatus)) printf("Child exit status: %d\n", WEXITSTATUS(wstatus)); else perror("Bad wait status\n"); // 父進(jìn)程退出 exit(PARENT_EXIT); } else if (pid == 0) { // 子進(jìn)程立即退出, 因此需要父進(jìn)程設(shè)置 WUNTRACED exit(CHILD_EXIT); } else { // 處理 fork 時(shí)出現(xiàn)的錯(cuò)誤 perror("fork\n"); exit(EXIT_FAILURE); } }
編譯并運(yùn)行上例可以得到被強(qiáng)轉(zhuǎn)后的狀態(tài)碼, 我們使用 WIFEXITED 判斷等待的子進(jìn)程是否執(zhí)行成功, 然后對(duì)執(zhí)行成功子進(jìn)程使用 WEXITSTATUS 獲取其退出狀態(tài). 對(duì)程序來(lái)說(shuō), 最終的退出狀態(tài)就是主進(jìn)程的退出狀態(tài).
> gcc ecitcode.c;./a.out;echo "Parent exit status: $?" Child exit status: 246 # -10 強(qiáng)轉(zhuǎn)為 uint8 Parent exit status: 102 # 10086 強(qiáng)轉(zhuǎn)為 uint8
在 POSIX 標(biāo)準(zhǔn)中規(guī)定退出狀態(tài) 0 代表該程序正常退出, 1 代表發(fā)生錯(cuò)誤, 其他數(shù)字由程序自行規(guī)定, 因此在 glibc 的 stdlib.h 中僅定義了如下宏:
#define EXIT_FAILURE 1 /* Failing exit status. */ #define EXIT_SUCCESS 0 /* Successful exit status. */
程序本身一般會(huì)在文檔中事先約定每種退出狀態(tài)代表的退出原因( termination ), 例如在 ls 的幫助文檔中:
> ls --help ...其他內(nèi)容... Exit status: # 退出狀態(tài) 0 if OK, # 正常執(zhí)行 1 if minor problems # 次要問(wèn)題, 例如: 無(wú)法訪問(wèn)子目錄 2 if serious trouble # 嚴(yán)重錯(cuò)誤, 例如: 無(wú)法訪問(wèn)命令行參數(shù) ...其他內(nèi)容...
命令的退出狀態(tài)
在 bash 中會(huì)記錄所執(zhí)行命令的退出狀態(tài), 可以通過(guò) $? 獲取最近執(zhí)行的命令的退出狀態(tài). bash 自身的退出狀態(tài)為執(zhí)行的最后一條命令的退出狀態(tài), 也就等價(jià)于顯式指定 exit $? . 如果沒(méi)有執(zhí)行任何命令就退出, 則 bash 的退出狀態(tài)為 0 , 要注意在 bash 中用 0 表示 true , 用非零表示 false .
# 用 exit 顯式指定退出狀態(tài) > bash > exit 98 exit > echo $? 98 # 什么也不執(zhí)行則退出狀態(tài)為 0 > bash exit # Ctrl + D 退出 > echo $? 0 # 默認(rèn)為最后一條命令的退出狀態(tài) > bash > ecasd ecasd: command not found exit # Ctrl + D 退出 > echo $? 127
在 bash 中對(duì)不同種類命令的退出狀態(tài)作出如下規(guī)定:
內(nèi)置命令: 由于內(nèi)置命令執(zhí)行時(shí)不需要啟動(dòng)額外的子進(jìn)程, 因此需要用返回值模擬退出狀態(tài). 每個(gè)函數(shù)都定義了自己的退出狀態(tài), 例如: 內(nèi)置命令 source 將腳本文件的最后一個(gè)命令的返回狀態(tài)作為命令的返回狀態(tài). bash 中所有的內(nèi)置命令都用退出狀態(tài) 2 表示用法錯(cuò)誤, 例如: 選項(xiàng)錯(cuò)誤, 缺少參數(shù).
> cd -+- # 錯(cuò)誤的參數(shù) bash: cd: -+: invalid option cd: usage: cd [-L|[-P [-e]] [-@]] [dir] > echo $? 2
外部命令: 外部命令的退出狀態(tài)就是使用 waitpid 得到的子進(jìn)程的退出狀態(tài), 如果子進(jìn)程在執(zhí)行過(guò)程被編號(hào)為 N 的信號(hào)所終止, 則得到的退出狀態(tài)就為 128+N .
Shell 函數(shù): 定義 shell 函數(shù)時(shí), 函數(shù)名與之前已定義的只讀函數(shù)名相同則退出狀態(tài)為 1 , 當(dāng)發(fā)生語(yǔ)法錯(cuò)誤則退出狀態(tài)為 2 . 執(zhí)行 shell 函數(shù)時(shí), 函數(shù)中最后執(zhí)行的一條命令的退出狀態(tài)就是整個(gè)函數(shù)的退出狀態(tài).
# 二次定義只讀函數(shù)報(bào)錯(cuò) > func () { echo; } > readonly -f func > func; echo $? 0 > func () { echo poi; } bash: func: readonly function > echo $? 1 # 定義函數(shù)發(fā)生語(yǔ)法錯(cuò)誤 > fune () {aa} bash: syntax error near unexpected token '{aa}' > echo $? 2 # 函數(shù)的退出狀態(tài)是最后執(zhí)行的命令的退出狀態(tài) > funr () { echo; return 6; } > funr; echo $? # echo 打印的空行 6 # return 6 是函數(shù)中最后執(zhí)行的命令
表達(dá)式: 使用 ((...)) 或 let 修飾的表達(dá)式的退出狀態(tài)取決于表達(dá)式的值, 如果表達(dá)式的值為 0 則退出狀態(tài)為 1 ; 如果表達(dá)式的值為非零, 則退出狀態(tài)為 0 .
> let 0+0; echo $? 1 # 表達(dá)式值為零 > ((7-5)); echo $? 0 # 表達(dá)式值非零
命令列表: 用 ; , & , && , || 連接命令被稱為命令列表, 其中用 && 和 || 連接的命令使用左關(guān)聯(lián)( left associativity )模式執(zhí)行列表中的命令. 整個(gè)命令列表的退出狀態(tài)為最后一條命令的退出狀態(tài). 此外, $( LISTS ) 以及流程控制結(jié)構(gòu)如: for , while 等的返回狀態(tài)也是結(jié)構(gòu)中的命令列表的退出狀態(tài).
# 功能: 能ping通baidu.com則輸出 `baidu.com is up` , 否則輸出 `baidu.com is down` 。 > ping -c1 baidu.com &> /dev/null && echo 'baidu.com is up' || echo 'baidu.com is down' baidu.com is down > echo $? 0 # 無(wú)論是否能 ping 通, 命令列表的退出狀態(tài)都等于最后一條命令的退出狀態(tài)
左關(guān)聯(lián)模式被廣泛應(yīng)用于各種語(yǔ)言的邏輯運(yùn)算符優(yōu)化中. 對(duì)于邏輯與運(yùn)算符 && , 以 eq1 && eq2 為例, 只有當(dāng)兩邊都為 True 才會(huì)返回 True , 因此當(dāng) eq1 為 False 時(shí), eq2 不會(huì)執(zhí)行; 對(duì)于邏輯或運(yùn)算符 || , 以 eq1 || eq2 為例, 只要兩邊有一個(gè) True 就會(huì)返回 True , 因此當(dāng) eq1 為 True 時(shí), eq2 不會(huì)執(zhí)行。
腳本: 使用 . 或 source 運(yùn)行腳本文件等同于在當(dāng)前 bash 中執(zhí)行代碼塊, 腳本中最后執(zhí)行的命令的退出狀態(tài)就是腳本的退出狀態(tài). 使用 ./腳本名 或 bash 腳本名 的方式執(zhí)行腳本文件等同于執(zhí)行外部命令, 腳本的退出狀態(tài)就是外部命令 bash 的退出狀態(tài). 如果腳本中最后執(zhí)行的命令是 exit , 那么使用 . 或 source 執(zhí)行該腳本文件在執(zhí)行結(jié)束后會(huì)退出當(dāng)前 bash .
后臺(tái)作業(yè)與協(xié)作進(jìn)程: 使用不帶選項(xiàng)的 wait 命令可以獲得最后一個(gè)執(zhí)行完畢的后臺(tái)作業(yè)的退出狀態(tài), 如果使用 wait -n <jobsec> 可以獲得指定后臺(tái)作業(yè)的退出狀態(tài), 如果作業(yè)不存在則退出狀態(tài)為 127 . 使用 coproc 在 sub shell 中執(zhí)行的命令的退出狀態(tài)和后臺(tái)作業(yè)一樣可以被 wait 獲取, coproc 自身的退出狀態(tài)始終為 0 .
> { sleep 10; aad; } & [1] 558 > wait -n 1 [1]+ Exit 127 { sleep 10; aad; } > coproc { sleep 10; aad; } [1] 558 > echo $? 0 # 這是 coproc 的執(zhí)行結(jié)果 > jobs [1]+ Exit 127 coproc COPROC { sleep 10; aad; }
管道命令: 默認(rèn)情況下, 管道的退出狀態(tài)取決于管道中最后一條命令的退出狀態(tài). 如果設(shè)置了 set -o pipefail , 那么只有在管道中的全部命令的退出狀態(tài)為 0 時(shí), 整個(gè)管道的退出狀態(tài)才為 0 , 否則就是最后一個(gè)非零的退出狀態(tài). 在管道前添加 ! 符號(hào)可以對(duì)整個(gè)管道的退出狀態(tài)取反. bash 中的特殊變量 $PIPESTATUS 以數(shù)組的形式存儲(chǔ)最近執(zhí)行的前臺(tái)管道的退出狀態(tài), 要注意的是單個(gè)命令也會(huì)被記錄, 也就是說(shuō) ${PIPESTATUS[0]} 和 $? 是等價(jià)的.
# 管道的退出狀態(tài)是最后一條命令的退出狀態(tài) > ps | xxp 2>/dev/null | cat; echo $? 0 > set -o pipefail > ps | xxp 2>/dev/null | cat; echo $? 127 # 設(shè)置了 pipefail 因此得到最后一個(gè)非零退出狀態(tài) # 管道中每個(gè)命令的退出狀態(tài)被按順序記錄在數(shù)組中 > easd 2>/dev/null | ls /nou 2>/dev/null | more 2>/dev/null > echo ${PIPESTATUS[@]} 127 2 0 # 不帶管道符號(hào)的單個(gè)命令也會(huì)被記錄 > ping asbasdasd 2>/dev/null; echo ${PIPESTATUS[0]} 2 > ping asbasdasd 2>/dev/null; echo $? 2
上述內(nèi)容就是如何在bash中退出狀態(tài)機(jī)制,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。