不管是在 Linux 下撰寫 Bash 指令檔或是在 Windows 下撰寫批次檔,最近一次程式執行的回傳代碼是非常重要的撰寫技巧這可以協助我們在撰寫自動化指令檔時能夠更有效的處理程式執行失敗的狀況,便可進一步撰寫條件式處理各種執行失敗的狀況,但是在 Windows 與 Linux 各有什麼異同之處呢?讓我們繼續看下去。
我們最近就寫了一支用來驗證網路目前的連線狀態的 Bash 指令檔,若網路發生斷線的狀況立即重新撥接 PPPoE 連線。撰寫這支程式的程式碼非常簡單,在 Bash 裡判斷 最近一次程式執行的回傳代碼 只要取得 $? 這個特殊變數即可,我們先做一個簡易的測試如下:
我們先執行 ping 命令:
root@localhost:~# ping -c 1 -W 10 168.95.1.1
PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data.
64 bytes from 168.95.1.1: icmp_seq=1 ttl=245 time=21.9 ms
--- 168.95.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 21.955/21.955/21.955/0.000 ms
接著我們直接輸出 $? 這個特殊變數,這時就可以得到一個 0 的結果:
root@localhost:~# echo $?
0
如果我們今天 ping 一個不存在的 IP 位址,得到的結果為 1
root@localhost:~# ping -c 1 -W 10 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
root@localhost:~# echo $?
1
如果我們今天 ping 一個錯誤的 IP 位址或不存在的 Domain Name 則會得到結果為 2
root@localhost:~# ping -c 1 -W 10 1.1.1.300
ping: unknown host 1.1.1.300
root@localhost:~# echo $?
2
root@localhost:~# ping -c 1 -W 10 doesntexist.com.tw
ping: unknown host doesntexist.com.tw
root@localhost:~# echo $?
2
不管哪一支程式或指令檔,在執行結束時都會回傳一個代碼,通常代碼為 0 就代表正常結束,不等於 0 就代表執行過程發生錯誤。
最後,我們判斷網路狀況的範例如下 ( Bash ):
#!/bin/bash
ping -W 10 168.95.1.1 > /dev/null 2>&1
if [ $? != 0 ]
then
printf "PPPoE was down!"
fi
相對的在 Winows 下一樣有類似的處理方式,在批次檔中只要取得 %ERRORLEVEL% 這個環境變數即可得知前一支程式所回傳的執行結果代碼,而且判斷的行為也與 Linux 完全相同,也就是代碼為 0 就代表正常結束,不等於 0 就代表執行過程發生錯誤。我們也來做一個簡易的測試,一樣使用 ping 程式:
C:\>ping -n 1 -w 10 168.95.1.1
Pinging 168.95.1.1 with 32 bytes of data:
Reply from 168.95.1.1: bytes=32 time=21ms TTL=244
Ping statistics for 168.95.1.1:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 21ms, Maximum = 21ms, Average = 21ms
C:\>echo %ERRORLEVEL%
0
如果我們 ping 一個不存在的位址,得到的回傳結果為 1
C:\>ping -n 1 -w 10 1.1.1.300
Ping request could not find host 1.1.1.300. Please check the name and try again.
C:\>echo %ERRORLEVEL%
1
最後,我們 Windows 版的判斷網路狀況的範例如下 ( Batch ):
@ECHO OFF
ping -n 1 -w 10 168.95.1.1 > nul
IF ERRORLEVEL 1 (
ECHO %date% %time% - Internet Connection Closed.
)
※ 注意事項 ※ ( 感謝 matthklo 網友的指正 )
在批次檔中的 IF ERRORLEVEL 語法,若傳入的數字為 1 代表的是【大於等於 1 的值都算是 True】,使用上要特別小心!
在 Windows 的 %ERRORLEVEL% 與 Linux 的 $? 雖然隸屬於不同作業系統平台,撰寫 Bash 與 Batch 的語法也不盡相同,不過使用方式與變數的目的都是一樣的,而每一支程式所設定的回傳代碼都是個別程式去設定的,沒有一定的規則,若有需要則需查詢各程式的詳細說明 (不過不見得都可以讓你查的到),唯一的共通之處就是預設回傳 0 代表執行無誤。
如果你撰寫 .NET 的 Console Application (主控台應用程式),預設的情況下 Main() 方法不需要回傳值,所以程式的執行結果其 %ERRORLEVEL% 永遠為 0,如下程式範例:
這時你可以將 Main() 方法修改為回傳 int 型態即可讓你的程式回傳適當執行結果狀態(Exit Status),也更能讓你的程式與批次檔搭配,讓撰寫自動化指令檔時可以得到更大的操作彈性。
最後,再補上一段 PowerShell 檢查 Exit Status 的方法,可想見的用法類似,差別僅在於語法與特殊變數的不同而已,在 Powershell 中要用 $LastExitCode 特殊變數即可取得最近一次程式執行的結束代碼。
不過,更神奇的是在 PowerShell 中竟然包含一個與在 Linux Bash 下一模一樣的 $? 特殊變數,其變數的型別為 Boolean 並非 Int32 喔,此變數只包含執行成功 ( $LastExitCode -eq 0 ) 或執行失敗 ( $LastExitCode -ne 0 ) 的狀態而已,不包括回傳的 Int32 代碼,使用時要特別注意!
相關連結