trap コマンドとは
実行中のプロセスに対するシグナルを検知し、指定された処理を返すコマンドです。
・・・と、言ってもよくわからないと思います。
特に「シグナル」。
シグナル とは
実行中のプロセスに対して、特定のイベントを通知するために送出されるものです。
よく使用されるのは、プロセスを終了するためのシグナルである SIGINT や SIGKILL です。
シグナル送出に良く利用するのは、kill
コマンドと、ショートカットで利用する Ctrl + C
ではないでしょうか。
kill -9 [Pid]
上記のコマンドは、プロセスの強制終了などで利用した事があると思います。
上記の強制終了の kill
コマンドは、オプションで -9
を指定しています。
-9
は SIGKILL
を意味し、指定したプロセスに対し強制終了のシグナルを送出します。
プロセスがこのシグナルを送られると、どんな状態であろうと強制終了させられます。
因みに、オプションを指定しない kill
コマンドには -15(SIGTERM:プロセス終了シグナル)
が指定されています。
Ctrl + C
は、Linux の処理の最中に押下する事で、その処理を中断させることが可能です。
送出しているシグナルは SIGINT
で、これはプロセスに割り込みを通知します。
SIGINT
は シグナル番号 -2
を意味します。
(指定したプロセスIDに割り込み、処理を強制的に Ctrl + C
に書き換える事で終了させているのでは?と考えています。この辺りは後々調べます。)
trap の書式
trap [-lp] [[arg] sigspec ...]
trap
はシグナルを送出した際に、指定したコマンドを実行させる事ができます。
書式は以下のようになっています。
trap 'コマンド' [シグナル番号|シグナル名]
コマンド例は、以下です。
trap 'echo trapped.' 2
上記のコマンドをCLIで実行後、Ctrl + C
を押下すると、trap
内で宣言された echo trapped
が実行されます。
$ trap 'echo trapped.' 2
# Ctrl + C を押下
$ ^Ctrapped.
-l
オプションを指定すると、使用可能なシグナル一覧が表示されます。
$ trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
上記のシグナル一覧は kill -l
でも確認ができます。
-p
オプションを指定すると、現在 trap
で指定した処理を確認できます。
$ trap -p
trap -- 'echo trapped.' SIGINT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
trap
の処理をリセットしたい場合は、trap [シグナル番号|シグナル名]
で可能です。
$ trap -p
trap -- 'echo trapped.' SIGINT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
$ trap 2
$ trap -p
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
trap
コマンドは大体把握できたでしょうか。
さて、このコマンドの活用方法を書いていきます。
trap コマンドの活用方法
こちらのコマンド、通常のCLIで使うことは中々無いかも知れません(私に使う機会が無いだけかもしれませんが・・・)。
ただ、シェルスクリプトを書く場合結構便利です。
この trap
コマンドをスクリプト中に宣言しておくと、処理の途中に Ctrl + C
を押下して中断した時、指定した処理を実行する事ができます。
貴方は新任のサーバ管理者だとしましょう。
貴方は複数のサーバのディスク使用量を df
コマンドを使って取得したいと考えています。
そのサーバには何故かsendmailやpostfixといったMTAがインストールされておらず、貴方はCronの存在も全く知りません(何しろ会社に入って数ヶ月です。PCの知識ゼロで、新人研修ではJavaの書き方とLinuxの基本的な操作方法と研修報告書と飲み会に時間を費やしていました)。
貴方は毎日決まった時間帯に手動で各サーバのディスク使用量を取得していましたが、ある時シェルスクリプトの存在を知り、今実施している作業が幾らか効率化できるのでは? と考えました。
また、 tee
コマンドを使って画面とファイルへ出力しようと考えました。
ついでに diff
を利用して前回の結果と、今回の結果との比較を出力したいと考えました。
貴方は一生懸命調べながらシェルスクリプトを書き上げました。
簡単に書き出すと以下のようになるかと思います。
#!/bin/bash
today=`date +%y%m%d`
dir=/usr/local/src/srvdf
fileA=`ls -tr ${dir} | tail -n 1`
ssh srv1 "echo ----srv1 disk free result---- ; df -h ; echo ''" | tee srvdf.${today}
ssh srv2 "echo ----srv2 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv3 "echo ----srv3 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv4 "echo ----srv4 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
mv srvdf.${today} ${dir}/srvdf.${today}
fileB=`ls -tr ${dir} | tail -n 1`
echo "----diff "${fileA} ${fileB}"----"
diff ${dir}/${fileA} ${dir}/${fileB}
知識に偏りが見られるのは新人だからです。
さて、 この dfResult.sh
ですが、処理の最中にスクリプトを中断したい場合どうすればいいでしょうか。
シグナル (Ctrl + C)
が送出できるのは次の4行です。
ssh srv1 "echo ----srv1 disk free result---- ; df -h ; echo ''" | tee srvdf.${today}
ssh srv2 "echo ----srv2 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv3 "echo ----srv3 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv4 "echo ----srv4 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh
コマンドで各サーバに接続し、df -h
で現在のディスク容量を出力します。
その後 tee
コマンドに渡し、標準出力と srvdf.YYMMDD
という名前でファイルに出力します。
SSH はパスワード認証という想定です。
その場合、パスワード認証の時に Ctrl + C
で中断が可能です。
しかし、中断した場合、 tee
にも既に結果を渡しているので、中断した部分までが出力されます。
最初の ssh
で中断した場合、何も出力されない為、空のファイルが作成されます。
それでは気持ち悪いですし、既にスクリプトを実行していた場合、空のファイルがカレントディレクトリに残ってしまいます。
(回避策は色々あると思いますが、何分新人なので・・・。)
ではどうするか、となった時に利用できるのが、先述した trap
コマンドです。
trap 'rm srvdf.${today} ; exit 1' 1 2 3 15
上記のコマンドをスクリプトに追加します。
因みに、各シグナル番号の意味は以下です。
番号 | シグナル名 | 意味 |
---|---|---|
1 | SIGHUP | プロセスに再起動を通知する。デーモンのリセットに使用される。 |
2 | SIGINT | プロセスに割り込みを通知する。Ctrl+Cを押したときに発生する。 |
3 | SIGQUIT | プロセスに終了を通知する。Ctrl+\ を押したときに発生する。 |
15 | SIGTERM | プロセスに終了を通知する。killコマンドはデフォルトでこのシグナルを指定している。 |
(2以外を指定する理由はわかりません。その内調査します。) |
追記箇所は、 ssh
コマンドの前ならばどこでも大丈夫です。
最初の方の行では、変数を宣言しているので、その後に入れるのが綺麗かもしれません。
追記すると以下のようになります。
#!/bin/bash
today=`date +%y%m%d`
dir=/usr/local/src/srvdf
fileA=`ls -tr ${dir} | tail -n 1`
trap 'rm srvdf.${today} ; exit 1' 1 2 3 15
ssh srv1 "echo ----srv1 disk free result---- ; df -h ; echo ''" | tee srvdf.${today}
ssh srv2 "echo ----srv2 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv3 "echo ----srv3 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
ssh srv4 "echo ----srv4 disk free result---- ; df -h ; echo ''" | tee -a srvdf.${today}
mv srvdf.${today} ${dir}/srvdf.${today}
fileB=`ls -tr ${dir} | tail -n 1`
echo "----diff "${fileA} ${fileB}"----"
diff ${dir}/${fileA} ${dir}/${fileB}
SIGINT
SIGHUP
SIGQUIT
SIGTERM
のシグナルがプロセスに対して送出された際に、rm
コマンドが実行され、ファイルが削除されます。
その後、セミコロン ;
で コマンドを連結させ、 exit 1
を実行しています。
exit
は終了ステータスを表し、 exit
コマンドが流れるとスクリプトの処理が終了します。
処理を中断しているので、エラーである1をステータスとして返しています。
これで貴方はシェルスクリプトを完成させることができました。おめでとう!
結構簡単でしかも足早ではありましたが、 trap
の使い方はなんとなくイメージできたでしょうか。
便利なコマンドだと思うのでぜひ使ってみてくださいね。
# trap コマンドのまとめ
trap の書式
trap 'コマンド' [シグナル番号|シグナル名]
コマンド例)
trap 'echo trapped.' 1 2 3 15
$ trap 'echo trapped.' 1 2 3 15
# Ctrl + C を押下
$ ^Ctrapped.
# シグナル送出時の処理をリセットする場合、 trap signal で実行する。
$ trap 1 2 3 15
# Ctrl + C を押下
$ ^C
trap のオプション
trap [-lp]
-l
シグナル一覧を表示する。
-p
trap で指定した処理を確認できる
オプション例)
$ trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
$ trap -p
trap -- 'echo trapped.' SIGHUP
trap -- 'echo trapped.' SIGINT
trap -- 'echo trapped.' SIGQUIT
trap -- 'echo trapped.' SIGTERM
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
この記事が誰かのお役に立てれば幸いです。