經過bash -x <script>
的方式能夠在調試模式下運行整個腳本, bash
會在在運行前打印出了每一行命令, 並且每行前面用+號代表命令的嵌套層數.linux
> bash -x debug.sh + echo 'First line' First line # 輸出結果沒有加號 ++ date # 先執行命令替換 兩個加號是由於該命令嵌套在echo中 + echo 'Print datetime: Thu 26 Mar 2020 08:21:28 PM CST Done.' Print datetime: Thu 26 Mar 2020 08:21:28 PM CST Done.
若是腳本比較複雜, 咱們能夠經過使用環境變量PS4
配合調試用的內置變量用於輸出更加詳細的信息:shell
> export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: ' > bash -x debug.sh +debug.sh:3:: echo 'First line' First line ++debug.sh:4:: date +debug.sh:4:: echo 'Print datetime: Thu 26 Mar 2020 08:35:59 PM CST Done.' Print datetime: Thu 26 Mar 2020 08:35:59 PM CST Done.
咱們也可使用trap
的DEBUG
關鍵字在解釋執行每一行腳本以前執行指定的命令或函數:bash
trap 'echo "VARIABLE-TRACE> \$variable = \"$variable\""' DEBUG variable=29 let variable++ let variable*=5 exit 0 # 輸出以下 VARIABLE-TRACE> $variable = "" VARIABLE-TRACE> $variable = "29" VARIABLE-TRACE> $variable = "30" VARIABLE-TRACE> $variable = "150"
還可使用trap
的ERR
關鍵字用於在解釋出現錯誤時執行預設的操做, 例如打印出錯的命令:函數
trap 'echo $BASH_COMMAND' ERR
但鑑於使用trap
進行調試的執行效率太低, 建議在較爲複雜的腳本中直接使用調試選項或者使用bashdb
等調試工具.工具
在函數中使用內置命令caller
可以把函數的調用信息輸出到stdout
, 但要注意該命令必須在函數內部調用.this
#!/usr/bin/bash func1 () { for i in `seq 0 3` do echo -e "Level$i\t `caller $i`" done } func2 () { func1 } func3 () { func2 } func3 caller 0 # 必須在函數中調用 不然無輸出 exit 0
運行該腳本能夠獲得如下輸出:命令行
Level0 11 func2 call.sh # func1 的直接調用者 Level1 15 func3 call.sh # 一層間接調用 Level2 18 main call.sh # 二層間接調用 Level3 # 無輸出 由於沒有第三層調用
能夠經過set
命令構造局部調試塊,咱們能夠按照以下方式添加局部調試:debug
set -x date set +x
> bash script1.sh # 不須要添加調試參數 The script starts now. + date Fri 28 Feb 2020 06:23:04 PM CST + set +x This is a string: black And this is a number: 9
短命令 | 長命令 | 效果 |
---|---|---|
set -f | set -o noglob | 對文件名停用元字符匹配 |
set -v | set -o verbose | 打印輸入的命令 |
set -x | set -o xtrace | 命令行首打印+ ,執行出錯會打印詳細信息 |
調試用的參數能夠在運行中動態疊加或刪除:調試
> set -v > date date Fri 28 Feb 2020 06:54:47 PM CST > set -x # 參數能夠累加 date # -v 的效果 + date # -x 的效果 Fri 28 Feb 2020 06:55:37 PM CST > set +vx # 取消參數 set +vx
經過使用-f
選項能夠顯著減小腳本中的轉義字符:code
> ls ? x86_64-pc-linux-gnu-library > set -f # 停用元字符匹配 > ls ? ls: cannot access '?': No such file or directory > touch ? > ls ? '?' > rm ? > set +f -x # 選項 x 還能夠用於顯示詳細錯誤信息 > aaa + aaa + '[' -x /usr/lib/command-not-found ']' + /usr/lib/command-not-found -- aaa Command 'aaa' not found, did you mean: command 'aha' from deb aha (0.5-1) command 'jaaa' from deb jaaa (0.8.4-4) command 'aa' from deb astronomical-almanac (5.6-6) Try: sudo apt install <deb name> + return 127
也能夠直接在腳本第一行添加參數讓腳本默認以調試模式啓動:
#!/bin/bash -xv
還能夠在可能出錯的命令前用echo
輸出調試信息:
echo "debug message: now attempting to start w command"; w # 用 ; 對要執行的命令排序 echo "Variable VARNAME is now set to $VARNAME."
爲了方便調試,咱們可使用set
命令對bash
的選項進行設置:
> set -o # 查看全部選項的開關狀態 > set -o | grep xtrace xtrace off > set -x # 等價於 set -o xtrace > set -o | grep xtrace + grep --color=auto xtrace + set -o xtrace on > set +x # 等價於 set +o xtrace + set +x > set -o | grep xtrace xtrace off
引用爲定義變量時報錯:
> unset $VAR;echo $VAR > set -u # 等價於 set -o nounset > echo $var bash: var: unbound variable
爲防止誤操做覆蓋文件中的數據, 設置禁止重定向到已經存在的文件:
> set -C # 等價於 set -o noclobber > touch test > date > test bash: test: cannot overwrite existing file
設置不解析通配符:
> set -f # 等價於 set -o noglob > touch * > ll * -rw-rw-r-- 1 remilia remilia 0 Mar 1 20:09 '*'