# Bash Script Debug mode ## 實作目標 將 Bash Function 裡面,命令的正確執行結果 + 錯誤輸出 + 真正執行的命令(變數會變成值) 通通都倒到同一個檔案中,不用手動一行一行在行尾加上 `& > /dev/null`,而且加上行號、function 名稱,好讓你以後除錯時超清楚! ## 注意事項 :::danger 1. 因 log file 的目錄區是在 `/var/log` 底下,所以以下範例需要由 root 使用者或 sudo user 執行。 2. log 的檔名前面有加 `.` 代表隱藏檔案,所以不能用 `ll`,大部分的 `ll` 都是 `alias ll="ls -l"`,要記得用 `ls -al` ::: ## 範例程式 ```bash= #!/bin/bash TRACE_LOG() { Command_log_file="/var/log/.$(basename "${BASH_SOURCE[0]}").log" exec >>"${Command_log_file}" 2>&1 exec 3>> "${Command_log_file}" export BASH_XTRACEFD=3 PS4='($BASH_SOURCE:$LINENO:$FUNCNAME): ' set -x } TEST() { TRACE_LOG for i in {1..10}; do echo ${i} done # failed command fdsffa fdsafasf set +x } TEST ``` ## 程式重點介紹 ```bash=3 TRACE_LOG() { ``` 這是宣告一個 Bash function,叫做 `TRACE_LOG`,之後你可以在 script 其他地方用 `TRACE_LOG` 來呼叫它。 ```bash=4 Command_log_file="/var/log/.$(basename "${BASH_SOURCE[0]}").log" ``` * 設定 log 檔的檔名與路徑。 * `$(basename "${BASH_SOURCE[0]}")` 會取得目前這個 script 的檔名,例如 `myscript.sh`。 * 結果會變成這樣的一個 log 路徑,例如: ``` /var/log/.myscript.sh.log ``` 這樣做的目的是:自動為每個 script 建立一個獨立的 log 檔,方便紀錄。 ```bash=5 exec >>"${Command_log_file}" 2>&1 ``` * 把所有之後的「正常輸出(stdout)」與「錯誤訊息(stderr)」全部寫進前面設定的 log 檔中。 * 從這一行開始,像 `echo` 或 `ls` 等指令的輸出都不會出現在螢幕上,而是寫進 log 檔。 ```bash=6 exec 3>> "${Command_log_file}" ``` * 開啟一個「新的資料管道」,叫做「檔案描述符 3(FD 3)」,也指向同一個 log 檔。 * 這行本身不會產生輸出,它只是預先準備好「FD 3」,之後會專門用來記錄除錯訊息(debug trace)。 ```bash=7 export BASH_XTRACEFD=3 PS4='($BASH_SOURCE:$LINENO:$FUNCNAME): ' ``` 這是在啟用 Bash 的「除錯模式」所需的變數。 * `BASH_XTRACEFD=3`:告訴 Bash,把除錯訊息(由 `set -x` 產生的 trace)寫入「檔案描述符 3」,也就是 log 檔。 * `PS4='($BASH_SOURCE:$LINENO:$FUNCNAME): '`:設定每一行除錯訊息的開頭格式,包括: * `$BASH_SOURCE`:是哪個 script 檔案 * `$LINENO`:是哪一行 * `$FUNCNAME`:是在哪個 function 裡 舉例產生的 log 可能會長這樣: ``` (myscript.sh:15:myfunc): echo "啟動中" ``` 這樣可以清楚知道程式執行到哪裡、哪一行。 ```bash set -x ``` * 這行開啟 Bash 的「指令追蹤模式(debug trace mode)」。 * 開啟後,Bash 每執行一行指令,都會先印出該指令的內容(寫入你上面指定的 log 檔,因為有設定 `BASH_XTRACEFD`)。 --- ## 使用方式 你只要在 script 裡面某個地方寫: ```bash TRACE_LOG ``` 從那一行開始,這支 script 的輸出和行為紀錄就會開始寫進 log 檔 `/var/log/.xxx.log`。 ## 查看 Log ``` # cat /var/log/.test.sh.log ``` 執行結果 : ``` (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 1 1 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 2 2 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 3 3 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 4 4 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 5 5 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 6 6 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 7 7 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 8 8 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 9 9 (./test.sh:17:test): for i in {1..10} (./test.sh:19:test): echo 10 10 (./test.sh:23:test): fdsffa ./test.sh: line 23: fdsffa: command not found (./test.sh:24:test): fdsafasf ./test.sh: line 24: fdsafasf: command not found (./test.sh:25:test): set +x ```