antony@notes:~/ops-automation$ cat "Bash-Script-Debug-mode.md"
Bash Script Debug mode
Bash Script Debug mode
實作目標
將 Bash Function 裡面,命令的正確執行結果 + 錯誤輸出 + 真正執行的命令(變數會變成值) 通通都倒到同一個檔案中,不用手動一行一行在行尾加上 & > /dev/null,而且加上行號、function 名稱,好讓你以後除錯時超清楚!
注意事項
:::danger
- 因 log file 的目錄區是在
/var/log底下,所以以下範例需要由 root 使用者或 sudo user 執行。 - log 的檔名前面有加
.代表隱藏檔案,所以不能用ll,大部分的ll都是alias ll="ls -l",要記得用ls -al:::
範例程式
#!/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程式重點介紹
TRACE_LOG() {這是宣告一個 Bash function,叫做 TRACE_LOG,之後你可以在 script 其他地方用 TRACE_LOG 來呼叫它。
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 檔,方便紀錄。
exec >>"${Command_log_file}" 2>&1- 把所有之後的「正常輸出(stdout)」與「錯誤訊息(stderr)」全部寫進前面設定的 log 檔中。
- 從這一行開始,像
echo或ls等指令的輸出都不會出現在螢幕上,而是寫進 log 檔。
exec 3>> "${Command_log_file}"- 開啟一個「新的資料管道」,叫做「檔案描述符 3(FD 3)」,也指向同一個 log 檔。
- 這行本身不會產生輸出,它只是預先準備好「FD 3」,之後會專門用來記錄除錯訊息(debug trace)。
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 "啟動中"這樣可以清楚知道程式執行到哪裡、哪一行。
set -x- 這行開啟 Bash 的「指令追蹤模式(debug trace mode)」。
- 開啟後,Bash 每執行一行指令,都會先印出該指令的內容(寫入你上面指定的 log 檔,因為有設定
BASH_XTRACEFD)。
使用方式
你只要在 script 裡面某個地方寫:
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