Edited at

bashデバッグTips

More than 3 years have passed since last update.



bashデバッグTips


モチベーション


  • デバッグしやすくしたい


1. Bashオプション(bash -uvx


  • 動作確認用スクリプト


cal_score_ave.sh

#!/bin/bash

: [DEBUG] set param
readonly SCORE_ARRAY=(100 90 80 70)
readonly COUNTS=${#SCORE_ARRAY[@]}

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
return 0
}

: [DEBUG] main
cal_score_ave
echo $score_ave
echo $param
echo "finished"

exit 0



-uvx

# bash -uvx cal_score_ave.sh

#!/bin/bash

: [DEBUG] set param
+ : '[DEBUG]' set param
readonly SCORE_ARRAY=(100 90 80 70)
+ SCORE_ARRAY=(100 90 80 70)
+ readonly SCORE_ARRAY
readonly COUNTS=${#SCORE_ARRAY[@]}
+ readonly COUNTS=4
+ COUNTS=4

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
return 0
}

: [DEBUG] main
+ : '[DEBUG]' main
cal_score_ave
+ cal_score_ave
+ local _score_sum=i
+ : '[DEBUG]' cal
+ for _score in '${SCORE_ARRAY[@]}'
cal_score_ave.sh: line 12: i: 展開されていない変数


オプション
説明

-u
未定義の変数をチェックしそこで処理を終了する

-v
実行するコマンドをそのまま表示する

-x
実行内容をトレースする


1.1 bash -u


-u

# bash -u cal_score_ave.sh

cal_score_ave.sh: line 12: i: 展開されていない変数


1.2 bash -v


-v

# bash -v cal_score_ave.sh

#!/bin/bash

: [DEBUG] set param
readonly SCORE_ARRAY=(100 90 80 70)
readonly COUNTS=${#SCORE_ARRAY[@]}

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
return 0
}

: [DEBUG] main
cal_score_ave
echo $score_ave
85
echo $param

echo "finished"
finished

exit 0



1.3 bash -x


-x

# bash -x cal_score_age.sh

+ : '[DEBUG]' set param
+ SCORE_ARRAY=(100 90 80 70)
+ readonly SCORE_ARRAY
+ readonly COUNTS=4
+ COUNTS=4
+ : '[DEBUG]' main
+ cal_score_ave
+ local _score_sum=i
+ : '[DEBUG]' cal
+ for _score in '${SCORE_ARRAY[@]}'
+ _score_sum=100
+ for _score in '${SCORE_ARRAY[@]}'
+ _score_sum=190
+ for _score in '${SCORE_ARRAY[@]}'
+ _score_sum=270
+ for _score in '${SCORE_ARRAY[@]}'
+ _score_sum=340
+ score_ave=85
+ return 0
+ echo 85
85
+ echo

+ echo finished
finished
+ exit 0



2. ヌルコマンド(:


: [arguments]

   何もしません。

   

   このコマンドはargumentsを展開し、指定されたリダイレクトを実行する以外には何も行いません。

   終了コード 0 を返します。

                                       man bash より引用



3. デバッグプロンプト(PS4


PS4

   このパラメータは PS1 と同じように展開されます。

   

   この値は実行トレース中に bash が表示する各コマンド前に出力されます。

   

   複数段の間接レベル (levels of indirection) を示すときは、

   PS4 の最初の文字が必要に応じて複数回表示されます。

   

   デフォルト値は ‘‘+ ’’ です。

                                       man bash より引用



  • PS4の変更



    • ~/.bashrcに以下1行追加




~/.bashrc

export PS4='+ (${BASH_SOURCE}:${LINENO}): ${FUNCNAME:+$FUNCNAME(): }'


# bash -x ./cal_score_ave.sh

+ (./cal_score_ave.sh:3): : '[DEBUG]' set param
+ (./cal_score_ave.sh:4): SCORE_ARRAY=(100 90 80 70)
+ (./cal_score_ave.sh:4): readonly SCORE_ARRAY
+ (./cal_score_ave.sh:5): readonly COUNTS=4
+ (./cal_score_ave.sh:5): COUNTS=4
+ (./cal_score_ave.sh:19): : '[DEBUG]' main
+ (./cal_score_ave.sh:20): cal_score_ave
+ (./cal_score_ave.sh:8): cal_score_ave(): local _score_sum=i
+ (./cal_score_ave.sh:9): cal_score_ave(): : '[DEBUG]' cal
+ (./cal_score_ave.sh:10): cal_score_ave(): for _score in '${SCORE_ARRAY[@]}'
+ (./cal_score_ave.sh:12): cal_score_ave(): _score_sum=100
+ (./cal_score_ave.sh:10): cal_score_ave(): for _score in '${SCORE_ARRAY[@]}'
+ (./cal_score_ave.sh:12): cal_score_ave(): _score_sum=190
+ (./cal_score_ave.sh:10): cal_score_ave(): for _score in '${SCORE_ARRAY[@]}'
+ (./cal_score_ave.sh:12): cal_score_ave(): _score_sum=270
+ (./cal_score_ave.sh:10): cal_score_ave(): for _score in '${SCORE_ARRAY[@]}'
+ (./cal_score_ave.sh:12): cal_score_ave(): _score_sum=340
+ (./cal_score_ave.sh:15): cal_score_ave(): score_ave=85
+ (./cal_score_ave.sh:16): cal_score_ave(): return 0
+ (./cal_score_ave.sh:21): echo 85
85
+ (./cal_score_ave.sh:22): echo

+ (./cal_score_ave.sh:23): echo finished
finished
+ (./cal_score_ave.sh:25): exit 0


4. trapコマンド

sigspec
タイミング

EXIT
シェルがスクリプトを終了した

ERR
コマンド、シェル関数から0以外の終了ステータスが返された

DEBUG
シェルが各コマンド、算術、シェル関数の最初のコマンドの実行前

RETURN
source または . で実行されたシェル関数/スクリプトが終了した


trap [-lp] [[arg] sigspec ...]

   シェルがシグナル sigspec を受け取ると、コマンド arg が読み込まれて、実行されます。

   

   arg が存在しない (かつ sigspec が一つ指定された) 場合か、 arg が - の場合、

   指定されたシグナルは全てオリジナルの動作 (シェルの起動時に設定されていた値) にリセットされます。

   

   arg が空文字列である場合、

   それぞれの sigspec で指定されたシグナルは、 シェルとシェルが起動したコマンドから無視されます。

   

   arg なしで -p オプションが与えられた場合、

   各 sigspec に関連付けられた trap コマンドが表示されます。

   

   引き数が全くないか、 -p だけが与えられた場合、

   trap は各シグナルに関連付けられたコマンドのリストを出力します。

   

   -l オプションを与えると、 シェルはシグナル名と対応する番号のリストを出力します。

   それぞれの sigspec は、 で定義されているシグナル名、またはシグナル番号です。

   シグナル名は大文字小文字は区別されず、先頭の SIG は省略可能です。

   

   sigspec が EXIT (0) であれば、シェルの終了時にコマンド arg が実行されます。

   

   sigspec が DEBUG であれば、

   各々の 単純なコマンド、for コマンド、case コマンド、select コマンド、各々の算術 for コマンドの前、

   およびシェル関数の最初のコマンドの実行前 (前述の シェルの文法セクションを参照) に、

   コマンド arg が実行されます。

   

   DEBUG のトラップの影響についての詳細は組み込みコマンド shopt の extdebug オプションの説明を

   参照してください。

   

   sigspec が RETURN であれば、シェル関数の実行、

   または組み込みコマンドの . や source で実行されたスクリプトの実行が終わるたびに

   コマンド arg が実行されます。

   

   sigspec が ERR であれば、 単純なコマンドが 0 以外の終了ステータスのときに

   コマンド arg が実行されます。

   ただし、失敗したコマンドが、 while または until キーワード直後のコマンドリストに含まれる場合、

   if 文の条件に含まれる場合、 && や || のリスト中で実行するコマンドに含まれる場合、

   および、コマンドの戻り値が ! で反転されている場合には、 ERR のトラップは実行されません。

   これらは errexit オプションが従う条件と同じです。

   

   シェルに入る際に無視されるシグナルは、トラップもリセットもできません。

   無視されなかったシグナルのトラップは、 サブシェルやサブシェル環境では作られたときに

   元の値にリセットされます。

   sigspec のいずれかが不正であれば、返却ステータスは偽になります。

   それ以外の場合には、 trap は真を返します。

                                       man bash より引用



4.1 ステップ実行


  • trapに渡すsigspecDEBUGを指定することでステップ実行できる

trap 'read -p "$0($LINENO) $BASH_COMMAND"' DEBUG`


trap動作確認コード

#!/bin/bash

: [DEBUG] set param
readonly SCORE_ARRAY=(100 90 80 70)
readonly COUNTS=${#SCORE_ARRAY[@]}

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
return 0
}

trap 'read -p "$0($LINENO) $BASH_COMMAND"' DEBUG
: [DEBUG] main
cal_score_ave
echo $score_ave
echo $param
trap – DEBUG
echo "finished"

exit 0



実行結果

# ./cal_score_ave.sh

./cal_score_ave.sh(5) : [DEBUG] set param
./cal_score_ave.sh(6) readonly SCORE_ARRAY=(100 90 80 70)
./cal_score_ave.sh(7) readonly COUNTS=${#SCORE_ARRAY[@]}
./cal_score_ave.sh(21) : [DEBUG] main
./cal_score_ave.sh(22) cal_score_ave
./cal_score_ave.sh(23) echo $score_ave
85
./cal_score_ave.sh(24) echo $param

./cal_score_ave.sh(25) echo "finished"
finished
./cal_score_ave.sh(27) exit 0


4.2 特定の箇所のみステップ実行する



  • trap - DEBUGで無効化する

trap 'read -p "$0($LINENO) $BASH_COMMAND"' DEBUG

# ここにステップ実行したい処理
trap - DEBUG


trap動作確認コード

#!/bin/bash

: [DEBUG] set param
readonly SCORE_ARRAY=(100 90 80 70)
readonly COUNTS=${#SCORE_ARRAY[@]}

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
return 0
}

trap 'read -p "$0($LINENO) $BASH_COMMAND"' DEBUG
: [DEBUG] main
cal_score_ave
echo $score_ave
echo $param
trap - DEBUG
echo "finished"

exit 0



実行結果

# ./cal_score_ave.sh

./cal_score_ave.sh(20) : [DEBUG] main
./cal_score_ave.sh(21) cal_score_ave
./cal_score_ave.sh(22) echo $score_ave
85
./cal_score_ave.sh(23) echo $param

./cal_score_ave.sh(24) trap - DEBUG
finished


4.3 エラーのみ捕捉する


  • trapに渡すsigspecERRを渡すことでエラーのみ捕捉することができる

trap 'read -p "$0($LINENO) $BASH_COMMAND":exit 1' ERR`


trap動作確認コード

#!/bin/bash

trap 'read -p "$0($LINENO) $BASH_COMMAND";exit 1' ERR

: [DEBUG] set param
readonly SCORE_ARRAY=(100 90 80 70)
readonly COUNTS=${#SCORE_ARRAY[@]}

function cal_score_ave() {
local _score_sum=i
: [DEBUG] cal
for _score in ${SCORE_ARRAY[@]}
do
_score_sum=$((_score_sum + _score))
done

score_ave=$((_score_sum / COUNTS))
#return 0
return 1
}

: [DEBUG] main
cal_score_ave
echo $score_ave
echo $param
echo "finished"

exit 0



実行結果

# ./cal_score_ave.sh

./cal_score_ave.sh(19) return 1