blog

ソフトウェア開発|スクリプトでBashシグナルキャプチャを使う

シグナルキャプチャを使うと、スクリプトが正常に実行されたかどうかにかかわらず、スクリプトをスムーズに終了させることができます。...

Oct 21, 2025 · 5 min. read
シェア

スクリプトの実行が成功しても失敗しても、スクリプトをスムーズに終了させることができます。

シェルスクリプトの開始を検出するのは難しくありませんが、シェルスクリプトの終了を検出するのはそれほど簡単ではありません。スクリプトが期待どおりに正常に終了するか、予期しないエラーで失敗するかを確かめる方法がないからです。スクリプトが失敗した場合、処理中の内容を記録しておくと便利ですが、それが不便な場合もあります。 trapコマンドは、スクリプトの終了シグナルをキャッチし、事前に定義された方法で応答することで、この問題を解決するために存在します。

レスポンス失敗

エラーが1つでも発生すると、エラーの連鎖につながる可能性があります。以下のスクリプト例では、最初に/tmpにテンポラリディレクトリを作成し、別の圧縮形式でパックする前に、テンポラリディレクトリでアンパックやファイル処理などの操作を実行できるようにしています:

  1. #!/usr/bin/env bash
  2. CWD=`pwd`
  3. TMP=${TMP:-/tmp/tmpdir}
  4. ## create tmp dir
  5. mkdir "${TMP}"
  6. ## extract files to tmp
  7. tar xf "${1}" --directory "${TMP}"
  8. ## move to tmpdir and run commands
  9. pushd "${TMP}"
  10. for IMG in *.jpg; do
  11. mogrify -verbose -flip -flop "${IMG}"
  12. tar --create --file "${1%.*}".tar *.jpg
  13. ## move back to origin
  14. ## bundle with bzip2
  15. bzip2 --compress "${TMP}"/"${1%.*}".tar \
  16. --stdout > "${1%.*}".tbz
  17. ## clean up
  18. /usr/bin/rm -r /tmp/tmpdir

通常、スクリプトは期待通りに実行されます。しかし、アーカイブ内のファイルが期待されるJPEGではなくPNGである場合、スクリプトは途中で失敗し、別の問題が発生します: 一時ディレクトリを削除する最後のステップが正しく実行されないのです。テンポラリディレクトリを手動で削除する場合は問題ありませんが、テンポラリディレクトリを手動で削除しない場合、このスクリプトの次の実行時に、予測できない残ファイルだらけの既存のテンポラリディレクトリを処理しなければなりません。

一つの解決策は、スクリプトの最初に予防的削除ロジックを追加して、この状況に対処することです。しかし、このやり方は少々乱暴で、この問題は構造的に解決すべきです。トラップを使うのはエレガントなアプローチです。

トラップによる信号の捕捉

プログラムの実行中にシグナルを捕捉するトラップができます。killコマンドやkillallコマンドを使ったことがある人は、すでにSIGTERMというシグナルを使っています。これに加えて、trap -lまたはtrap --listを実行すると、さらに多くのシグナルをリストアップできます:

  1. $ trap --list
  2.  1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
  3.  6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
  4. 11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
  5. 16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
  6. 21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
  7. 26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
  8. 31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
  9. 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
  10. 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
  11. 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
  12. 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
  13. 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
  14. 63) SIGRTMAX-1  64) SIGRTMAX

トラップが認識できるシグナルは上記以外にもあります:

  • EXIT: プロセス終了時にプロセスから通知されます。
  • ERR: プロセスがゼロ以外のステータスコードで終了したときに発せられるシグナル
  • DEBUGデバッグモードを示すブール値

Bashでシグナル・トラッピングを実装したい場合は、トラップ後に実行するコマンドと、トラップするシグナルのリストを追加するだけです。

たとえば、次の行は、プロセスの実行中にユーザーが Ctrl + C キーの組み合わせを押したときに発せられる SIGINT シグナルをキャプチャします:

  1. trap "{ echo 'Terminated with Ctrl+C'; }" SIGINT

したがって、SIGINT、SIGTERM、Process Error Exit、Process Normal Exitなどのシグナルを捕捉するtrapを使用し、一時ディレクトリを正しく処理することで、上記のスクリプトの欠陥を修正することができます:

  1. #!/usr/bin/env bash
  2. CWD=`pwd`
  3. TMP=${TMP:-/tmp/tmpdir}
  4. "{ /usr/bin/rm -r "${TMP}" ; exit 255; }" \
  5. SIGINT SIGTERM ERR EXIT
  6. ## create tmp dir
  7. mkdir "${TMP}"
  8. tar xf "${1}" --directory "${TMP}"
  9. ## move to tmp and run commands
  10. pushd "${TMP}"
  11. for IMG in *.jpg; do
  12. mogrify -verbose -flip -flop "${IMG}"
  13. tar --create --file "${1%.*}".tar *.jpg
  14. ## move back to origin
  15. ## zip tar
  16. bzip2 --compress $TMP/"${1%.*}".tar \
  17. --stdout > "${1%.*}".tbz

より複雑な関数については、 使用してトラップ文を簡略化することもできます。

Bashでの信号キャプチャ

シグナルキャプチャは、スクリプトがすべてのタスクを正常に実行したかどうかにかかわらず、正しくクリーンアップを行うことを可能にし、スクリプトの信頼性を高めることができます。あなたのスクリプトにシグナルキャプチャを追加してみてください。

Read next