Bashスクリプトのデバッグ

概要

このチュートリアルでは、Bashシェルスクリプトをデバッグするためのさまざまなテクニックを見ていきます。 Bashシェルは組み込みのデバッガを提供しません。 ただし、この目的のために利用できる特定のコマンドおよび構成要素があります。

まず、スクリプトをデバッグするためのsetコマンドの使用法について説明します。 その後、setコマンドとtrapコマンドを使用して、特定のユースケースをデバッグするいくつかを確認します。 最後に、既に実行中のスクリプトをデバッグする方法をいくつか紹介します。

Bashデバッグオプション

Bashシェルで使用可能なデバッグオプションは、複数の方法でオンとオフを切り替えることができます。 スクリプト内では、setコマンドを使用するか、shebang行にオプションを追加することができます。 ただし、別の方法として、スクリプトの実行中にコマンドラインでデバッグオプションを明示的に指定する方法があります。 のは、議論に飛び込むしてみましょう。

2.1. 詳細モードを有効にする

-vスイッチを使用して詳細モードを有効にすることができます。

これを実証するために、サンプルスクリプトを作成しましょう:

#! /bin/bashread -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi

このスクリプトは、入力として入力された数値が正であるかどうかをチェックします。

次に、スクリプトを実行してみましょう:

$ bash -v ./positive_check.sh#! /bin/bashread -p "Enter the input: " valEnter the input: -10zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fiThe input value is not positive.

私たちが気づくことができるように、それはそれが処理される前に、端末上のスクリプトのすべての行を印刷します。

shebang行に-vオプションを追加することもできます:

#! /bin/bash -v

これは、bash-vを使用してスクリプトを明示的に呼び出すのと同じ効果があります。setコマンドを使用してスクリプト内でモードを有効にすることも:

#! /bin/bashset -v

実際には、上記のいずれかの方法を使用して、今後説明するさまざまなスイッチを有効にすることができます。

2.2. Noexecモードを使用した構文チェック

スクリプトを実行する前に構文的に検証したい場合があります。 その場合、-nオプションを使用してnoexecモードを使用できます。 その結果、Bashはコマンドを読み取りますが、それらを実行しません。

私たちを実行してみましょうpositive_check.sh noexecモードのスクリプト:

$ bash -n ./positive_check.sh

構文エラーがないため、空白の出力が生成されます。 次に、スクリプトを少し変更してthenステートメントを削除します:

#! /bin/bashread -p "Enter the input: " valzero_val=0if echo "Positive number entered."else echo "The input value is not positive."fi

次に、-nオプションを使用して構文的に検証します:

$ bash -n ./positive_check_noexec.sh./positive_check_noexec.sh: line 6: syntax error near unexpected token `else'./positive_check_noexec.sh: line 6: ` else'

予想どおり、if条件でthen文を逃したため、エラーがスローされました。

2.3. Xtraceモードを使用したデバッグ

前のセクションでは、スクリプトの構文エラーをテストしました。 しかし、論理エラーを識別するために、実行プロセス中の変数とコマンドの状態を追跡することができます。 このような場合は、-xオプションを使用してxtrace(execution trace)モードでスクリプトを実行できます。

このモードは、展開された後で実行される前の各行のコマンドのトレースを出力します。

私たちを実行してみましょうpositive_check.sh 実行トレースモードのスクリプト:

$ bash -x ./positive_check.sh+ read -p 'Enter the input: ' valEnter the input: 17+ zero_val=0+ ''+ echo 'Positive number entered.'Positive number entered.

ここでは、実行前にstdoutの変数の拡張バージョンを見ることができます。 +記号の前にある行は、xtraceモードによって生成されることに注意することが重要です。

2.4. 設定されていない変数の識別

のは、Bashスクリプトで設定されていない変数のデフォルトの動作を理解するための実験を実行してみましょう:

#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))echo $total

ここで、上記のスクリプトを実行します:

$ ./add_values.sh5

私たちが気づくことができるように、問題があります:スクリプトは正常に実行されましたが、出力は論理的に正しくありません。

-uオプションを指定してスクリプトを実行します:

$ bash -u ./add_values.sh./add_values.sh: line 4: tow_val: unbound variable

確かに、今より多くの明快さがあります!

変数tow_valが定義されていないため、スクリプトの実行に失敗しました。 合計を計算している間、誤ってtwo_valをtow_valと入力しました。

-uオプションは、パラメータ展開を実行するときに、未設定の変数とパラメータをエラーとして扱います。 その結果、-uオプションを指定してスクリプトを実行している間に変数が値にバインドされていないというエラー通知が表示されます

シェルスクリプトをデバッグするためのユースケース

これまでは、スクリプトをデバッグするためのさまざまなスイッチを見ました。 今後は、シェルスクリプトでこれらを実装するためのいくつかのユースケースとメソッドを見ていきます。

3.1. デバッグオプションの組み合わせ

より良い洞察を得るために、setコマンドのさまざまなオプションをさらに組み合わせることができます。

私たちを実行してみましょうadd_values.sh -vオプションと-uオプションの両方を有効にしたスクリプト:

$ bash -uv ./add_values.sh#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))./add_values.sh: line 4: tow_val: unbound variable

ここでは、-uオプションを指定してverboseモードを有効にすることで、エラーの原因となるステートメントを簡単に識別できます。

同様に、verboseモードとxtraceモードを組み合わせて、より正確なデバッグ情報を得ることができます。

前に説明したように、-vオプションは評価される前に各行を示し、-xオプションは展開された後に各行を示します。 したがって、-xオプションと-vオプションの両方を組み合わせて、変数置換の前後の文がどのように見えるかを確認できます。

今、私たちの実行してみましょうpositive_check.sh -xおよび-vモードを有効にしたスクリプト:

$ bash -xv ./positive_check.sh#! /bin/bashread -p "Enter the input: " val+ read -p 'Enter the input: ' valEnter the input: 5zero_val=0+ zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi+ ''+ echo 'Positive number entered.'Positive number entered.

変数展開の前後にステートメントがstdoutに出力されていることがわかります。

3.2. スクリプトの特定の部分のデバッグ

-xまたは-vオプションを使用したデバッグシェルスクリプトは、stdout上のすべての文の出力を生成します。 ただし、デバッグ情報をスクリプトの特定の部分だけに減らしたい場合があります。 これを実現するには、コードブロックを開始する前にデバッグモードを有効にし、後でsetコマンドを使用してリセットします。

例で確認してみましょう:

#! /bin/bashread -p "Enter the input: " valzero_val=0set -xif then echo "Positive number entered."else echo "The input value is not positive."fiset +xecho "Script Ended" 

ここでは、条件が開始される前にsetステートメントを使用してif条件のみをデバッグできます。 その後、set+xコマンドを使用してifブロックが終了した後にxtraceモードをリセットできます。

のは、出力でそれを検証してみましょう:

$ ./positive_debug.shEnter the input: 7+ ''+ echo 'Positive number entered.'Positive number entered.+ set +xScript Ended

確かに、出力はあまり雑然と見えます。

3.3. デバッグ出力のみをファイル

にリダイレクトする前のセクションでは、スクリプトの特定の部分だけにデバッグを制限する方法を検討しました。 その結果、stdoutの出力量を制限することができます。

さらに、デバッグ情報を別のファイルにリダイレクトし、スクリプト出力をstdoutに出力させることができます。

それをチェックするために別のスクリプトを作成してみましょう:

#! /bin/bashexec 5> debug.log PS4='$LINENO: ' BASH_XTRACEFD="5" read -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi

まず、デバッグを開きました。execコマンドを使用して書き込むためのファイル記述子(FD)5上のログファイル。

その後、特別なシェル変数PS4を変更しました。 PS4変数は、xtraceモードでシェルスクリプトを実行するときに表示されるプロンプトを定義します。 PS4のデフォルト値は+です。 PS4変数の値を、デバッグプロンプトに行番号を表示するように変更しました。 これを実現するために、別の特別なシェル変数LINENOを使用しました。

後で、BASH変数BASH_XTRACEFDにFD5を割り当てました。 実際には、BashはFD5にxtrace出力、つまりdebugを書き込みます。ログファイル。 スクリプトを実行してみましょう:

$ bash -x ./debug_logging.sh+ exec+ PS4='$LINENO: '4: BASH_XTRACEFD=5Enter the input: 2Positive number entered.

予想どおり、デバッグ出力は端末に書き込まれません。 ただし、最初の数行は、FD5がデバッグ出力に割り当てられるまで印刷された。

さらに、スクリプトは出力ファイルのデバッグも作成します。デバッグ情報を含むログ:

$ cat debug.log5: read -p 'Enter the input: ' val6: zero_val=07: ''9: echo 'Positive number entered.'

trapを使用したデバッグスクリプト

Bashのデバッグトラップ機能を利用して、コマンドを繰り返し実行できます。 Trapコマンドの引数で指定されたコマンドは、スクリプト内の後続の各ステートメントの前に実行されます。

これを例で説明しましょう:

#! /bin/bashtrap 'echo "Line- ${LINENO}: five_val=${five_val}, two_val=${two_val}, total=${total}" ' DEBUGfive_val=5two_val=2total=$((five_val+two_val))echo "Total is: $total"total=0 && echo "Resetting Total"

この例では、変数five_val、two_val、およびtotalの値を出力するechoコマンドを指定しました。 その後、このechoステートメントをDEBUG信号を使用してtrapコマンドに渡しました。 実際には、スクリプト内のすべてのコマンドを実行する前に、変数の値が出力されます。

生成された出力をチェックしてみましょう:

$ ./trap_debug.shLine- 3: five_val=, two_val=, total=Line- 4: five_val=5, two_val=, total=Line- 5: five_val=5, two_val=2, total=Line- 6: five_val=5, two_val=2, total=7Total is: 7Line- 7: five_val=5, two_val=2, total=7Line- 7: five_val=5, two_val=2, total=0Resetting Total

既に実行中のスクリプトのデバッグ

これまで、シェルスクリプトを実行中にデバッグする方法を紹介しました。 ここでは、既に実行中のスクリプトをデバッグする方法を見ていきます。

無限のwhileループでsleepを実行するサンプル実行スクリプトを考えてみましょう:

#! /bin/bashwhile :do sleep 10 & echo "Sleeping for 4 seconds.." sleep 4done

pstreeコマンドの助けを借りて、私たちは私たちのスクリプトによってフォークされた子プロセスを確認することができますsleep.sh:

$ pstree -pinit(1)─┬─init(148)───bash(149)───sleep.sh(372)─┬─sleep(422) │ ├─sleep(424) │ └─sleep(425) ├─init(213)───bash(214)───pstree(426) └─{init}(7)

追加のオプション-pを使用して、プロセスidとプロセス名を出力しました。 したがって、スクリプトが子プロセス(sleep)が完了するのを待っていることを認識することができます。

時には、プロセスによって実行される操作を詳しく見たいと思うかもしれません。 このような場合、straceコマンドを使用して、進行中のLinuxシステムコールをトレースできます:

$ sudo strace -c -fp 372strace: Process 372 attachedstrace: Process 789 attachedstrace: Process 790 attached^Cstrace: Process 372 detachedstrace: Process 789 detachedstrace: Process 790 detached% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------100.00 0.015625 5208 3 wait4 0.00 0.000000 0 6 read 0.00 0.000000 0 1 write 0.00 0.000000 0 39 close 0.00 0.000000 0 36 fstat 0.00 0.000000 0 38 mmap 0.00 0.000000 0 8 mprotect 0.00 0.000000 0 2 munmap 0.00 0.000000 0 6 brk 0.00 0.000000 0 16 rt_sigaction 0.00 0.000000 0 20 rt_sigprocmask 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 6 6 access 0.00 0.000000 0 1 dup2 0.00 0.000000 0 2 getpid 0.00 0.000000 0 2 clone 0.00 0.000000 0 2 execve 0.00 0.000000 0 2 arch_prctl 0.00 0.000000 0 37 openat------ ----------- ----------- --------- --------- ----------------100.00 0.015625 228 6 total

ここでは、オプション-pを使用して、プロセスid(372)、つまり実行中のスクリプトにアタッチしました。 さらに、-fオプションを使用してすべての子プロセスにアタッチしました。 Straceコマンドは、すべてのシステムコールの出力を生成することに注意してください。 したがって、straceの終了時にシステムコールの要約を出力するために-cオプションを使用しました。

まとめ

このチュートリアルでは、シェルスクリプトをデバッグするための複数の手法を検討しました。

最初に、setコマンドのさまざまなオプションと、スクリプトのデバッグに使用する方法について説明しました。 その後、いくつかのケーススタディを実装して、デバッグオプションの組み合わせを検討しました。 これに加えて、デバッグ出力を制限して別のファイルにリダイレクトする方法も検討しました。

次に、デバッグシナリオのためのtrapコマンドとデバッグ信号のユースケースを示しました。 最後に、既に実行中のスクリプトをデバッグするためのいくつかのアプローチを提供しました。

コメントを残す

メールアドレスが公開されることはありません。

Previous post 彼の王に挑む:アルスフの戦いがどのように勝利したか
Next post 鳥の目のビューの写真のヒント