hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

bashスクリプトのデバッグ方法



1 はじめに

bashスクリプトデバッグ方法についてまとめました。デバッグ方法は以下の2つがあります。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 8.6 (Sky Tiger)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
4.18.0-372.9.1.el8.x86_64

bashの版数は以下のとおりです。

[root@server ~]# bash --version
bash --version
GNU bash, バージョン 4.4.20(1)-release (x86_64-redhat-linux-gnu)
-snip-

3 スクリプトデバッグオプションを設定する方法

3.1 スクリプトが実行する処理を表示する方法(-x)

"-x"は、スクリプトが実行する処理を表示するオプションです。このオプションは、スクリプトの1行目のシバンに設定します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash -x

if [ "$1" -eq 1 ]; then
  echo 'one'
elif [ "$1" -eq 2 ]; then
  echo 'two'
else
  echo 'other'
fi

スクリプトを実行します。スクリプトが実行した処理が表示されます。

[root@server ~]# ./tp.sh 3
+ '[' 3 -eq 1 ']'
+ '[' 3 -eq 2 ']'
+ echo other
other

3.2 エラーが発生したらスクリプトを終了する方法(set -e/set +e)

"-e"は、スクリプト実行中にエラーが発生した場合、実行を終了するオプションです。これにより、エラーが発生した場所を迅速に特定することができます。一方で、"+e"は停止する動作を無効にするオプションです。

3.2.1 スクリプト全体に適用する方法(set -e)

2番目のlsコマンドが存在しないコマンドパスに対して実行するスクリプトを作成します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

ls /usr/bin/bash
ls /usr/bin/1111
ls /usr/bin/date

スクリプトを実行します。2つ目のlsコマンド実行時にエラーが発生しましたが、スクリプトは最後まで実行されました。

[root@server ~]# ./tp.sh
/usr/bin/bash
ls: '/usr/bin/1111' にアクセスできません: そのようなファイルやディレクトリはありません
/usr/bin/date

スクリプト先頭にset -eを追加します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

set -e
ls /usr/bin/bash
ls /usr/bin/1111
ls /usr/bin/date

スクリプトを実行します。2つ目のlsコマンドの実行時にエラーが発生し、そのためスクリプトが終了したことがわかります。

[root@server ~]# ./tp.sh
/usr/bin/bash
ls: '/usr/bin/1111' にアクセスできません: そのようなファイルやディレクトリはありません
3.2.2 スクリプトの一部分に適用する方法(set -e/set +e)

lsコマンドの前後に"-e"オプションと"+e"オプションを指定したスクリプトを作成します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

set -e
ls /usr/bin/bash
set +e
ls /usr/bin/1111
ls /usr/bin/date

スクリプトを実行します。-e/+eオプションは、1つ目のlsコマンドまでの有効範囲に限られるため、2つ目のlsコマンドでエラーが発生しても、スクリプトは停止しません。

[root@server ~]# ./tp.sh
/usr/bin/bash
ls: '/usr/bin/1111' にアクセスできません: そのようなファイルやディレクトリはありません
/usr/bin/date

3.3 変数未設定を検出する方法(-u)

"-u"オプションを指定したスクリプトを作成します。このスクリプトは、変数に値が設定されていない場合に警告を表示します。例として、変数var1に値を設定し、一方で変数var2には値を設定しません。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

var1="xxx"
echo ${var1}
echo ${var2}

スクリプトを実行します。変数var1に設定した値のみが表示されていることがわかります。

[root@server ~]# ./tp.sh
xxx

[root@server ~]#

スクリプトの終了ステータスを確認すると、正常終了(ステータスコード: 0)であることがわかります。

[root@server ~]# echo $?
0

次に、"-u"オプションを設定したスクリプトを作成します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

set -u

var1="xxx"
echo ${var1}
echo ${var2}

スクリプトを実行します。

[root@server ~]# ./tp.sh
xxx
./tp.sh: 行 7: var2: 未割り当ての変数です

スクリプトの終了ステータスを確認すると、異常終了(ステータスコード: 0以外)であることが確認できます。

[root@server ~]# echo $?
1

3.4 パイプ処理でエラーが発生したらパイプ処理全体でエラーにする方法(-u)

スクリプト内で参照するためのテスト用ファイルを作成します。

[root@server ~]# cat log.txt
111

パイプライン中のgrepコマンドは異常終了しますが、その後のsortコマンドが正常終了するテスト用のスクリプトを作成します。なお、なお、"-u"オプションは未設定とします。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

cat log.txt | grep xxx | sort

スクリプトを実行します。

[root@server ~]# ./tp.sh

grepコマンドは異常終了しますが、sortコマンドが正常終了するため、パイプ全体の終了ステータスは正常終了(ステータスコード: 0)となります。そのため、スクリプトの終了ステータスを確認すると、0であることがわかります。

[root@server ~]# echo $?
0

次に、"-u"オプションを設定したスクリプトを作成します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

set -o pipefail
cat log.txt | grep xxx | sort

スクリプトを実行します。

[root@server ~]# ./tp.sh

スクリプトの終了ステータスを確認すると、1であることがわかります。

[root@server ~]# echo $?
1

4 bashコマンドの引数にデバッグオプションを指定する方法

スクリプトに設定したオプションは、bashコマンドの引数として使用することができます。例えば、"-x"オプションを使用すると、以下のようになります

テスト用のスクリプトを作成します。

[root@server ~]# cat tp.sh
#!/usr/bin/bash

if [ "$1" -eq 1 ]; then
  echo 'one'
elif [ "$1" -eq 2 ]; then
  echo 'two'
else
  echo 'other'
fi

bashコマンドの引数として"-x"を指定すると、スクリプトの実行過程で実行されたコマンドが表示されるため、スクリプトの実行内容を確認することができます。

[root@server ~]# bash -x tp.sh 3
+ '[' 3 -eq 1 ']'
+ '[' 3 -eq 2 ']'
+ echo other
other

5 デバッガを使う方法(bashdb)

詳細は以下を参照してください。
bashdbの使い方 - hana_shinのLinux技術ブログ

6 スクリプトの構文をチェックする方法(ShellCheck)

詳細は以下を参照してください。
ShellCheckコマンドの使い方 - hana_shinのLinux技術ブログ