Help us understand the problem. What is going on with this article?

bashデバッグTips

More than 5 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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした