2013年1月19日土曜日

bashでSIGCHLDをハンドル

trapコマンドを使用するとシグナル受けて動作することができる。

#!/bin/bash
trap 'echo SIGINT trapped.' SIGINT
sleep 1000
スクリプトを実行後Ctrl+Cを入力するとechoが表示される

^CSIGINT trapped.

また、bashでは擬似シグナルという仕組みが実装されており、
bashの終了やコマンドのエラーをtrapすることができる。

#!/bin/bash

trap 'echo ScriptExit.' EXIT
echo "Hello"

スクリプトの最終行のHello実行後にEXITがtrapされている。

Hello
ScriptExit.

で、ここからが本題。
この仕組みを利用してSIGCHLD(子プロセスの終了)をハンドリングしたかったのだけど
意図したとおりに動かない。
こんな感じ。

#!/bin/bash

trap 'echo GetSIGCHLD.' SIGCHLD
echo "SCIRPT_END"

期待動作はSCIRPT_ENDの前にGetSIGCHLDが表示されて欲しいが、 実際の出力結果は以下のとおり

PID TTY          TIME CMD
20082 pts/3    00:00:00 bash
20486 pts/3    00:00:00 sample.script
20487 pts/3    00:00:00 ps
SCIRPT_END

SIGCHLDがtrapされていないように見える。
で、いろいろ調べてどうもbashには動作モードがあり
モニターモードに設定していないとSIGCHLDがtrapできないらしい。
それを修正すると。
#!/bin/bash

set -m  # monitor mode set
trap 'echo GetSIGCHLD.' SIGCHLD
ps
echo "SCIRPT_END"

PID TTY          TIME CMD
20082 pts/3    00:00:00 bash
20500 pts/3    00:00:00 sample.scrip
20501 pts/3    00:00:00 ps

GetSIGCHLD.

SCIRPT_END

期待通り!
今度bashのモードについて調べてみよう。

Bashの間接参照と配列

最近bashのスクリプトを書く機会が増えてきたのでメモ。
Bashでは変数に格納された文字列をほかの変数名とみなしてアクセスすることが可能
#!/bin/bash
BASE="Apple" 
echo ${BASE} 
REF="BASE"  
echo ${REF} 
echo ${!REF} 
このスクリプトの出力結果は以下のとおり。
~$./ref.sh
Apple
BASE
Apple

ただし、問題がひとつ、配列の場合は期待した結果が得られない。
#!/bin/bash                                

BASE=("Apple" "Orange" "Grape") 
echo ${BASE[@]}                            
REF="BASE"                                 
echo ${REF}                                
echo ${!REF[@]}                            
~$./ref_arrey.sh
Apple Orange Grape
BASE
0

これを解決するためにはevalを使用する。
#!/bin/bash

BASE=("Apple" "Orange" "Grape")            
echo ${BASE[@]}                            
REF="BASE"                                 
echo ${REF}                                
eval REF_ARRAY="\${${REF}[@]}"             
echo ${REF_ARRAY}    
~$./ref_arrey.sh
Apple Orange Grape
BASE
Apple Orange Grape

あんまりきれいな解決法じゃないなぁ。