Linuxのサービス制御シェルスクリプトを書いていて思ったことなんですけどね。そのプロジェクトの先人たちが書いたコードを拝見すると、一行目のシェバング行に sh と bash が混じっていたんですよ。つまり、「#!/bin/sh」と「#!/bin/bash」の両方のパターンがあったわけです。まあDebian系なので、shは正確にはashなんですがね。
それで、どういうふうに使い分けるんだろうか、と気になったわけです。shとbashではbashが拡張した独自文法がある、ということは知ってましたが、じゃあ具体的にはなんなのかと。色々調べてみたり、bashのmanページを見たりしましたが、違いをまとめたものがなかなか見つかりません。
だったら、機能が多いbashに統一すればいいのか、というとそうでもありません。bashのmanページの最後、バグの項は次の一文で始まります。
「bash は大きすぎるし、遅すぎます。」
おいおい(笑)公式でそう言っちゃうんかい。しかもmanページのバグの項目で。まあともかく、重いってことですね。まあ、起動スクリプトは軽いに越したことはありませんから、むやみやたらにbashに統一というのはあまり良くはないでしょう。
でまあ、どうしたかというと、どうしても sh では実現できない、あるいはbashを使わないと著しく大変な時だけbashを使い、それ以外の時はshで済ますことにしました。
ちなみに今までに書いたスクリプトの中にもシェバング行が「#!/bin/bash」のやつもあったので、そいつらは「#!/bin/sh」に書き換えてしまって不具合が出ない限りはそれでいく方針に。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年4月22日火曜日
2014年4月7日月曜日
シェルスクリプトでnginx master processのpidを取得する
シェルスクリプトからnginxの起動・終了を制御したいことがあって、master process のPIDを取得するにはどうしたらいいのか調べた経緯を少々書いてみます。
nginx は master process というプロセスの小プロセスとして worker process を複数持ち、実際の処理はworker processが担当し、全体の起動・終了などのworker process の管理はmaster processが担当しています。
(参考→http://d.hatena.ne.jp/kiwanami/20111205/1323105070)
ということで、シェルスクリプトから終了させる場合には、master processにシグナルを送るのが正しい作法でしょう。pkillでnginx全体にシグナル送ってもいいかもしれませんが、せっかくmaster processがあるのに少々乱暴っぽいですし。
普通に起動・終了を制御するのであればngixの設定でpidfileを作成していると思うのでpidfileに記録されてるpidを見ればmaster processのpidを特定できますけれども、pidfileが何らかの理由で消えたか見つからないか読み取れない場合の処理を書いておきたいと思ったので、pidfile以外でmaster processのpidを見つける方法をとります。
ということで、以下のコマンドで取得できます。
ps の a オプションは端末のある全てのプロセスの表示、xオプションは端末のないプロセスの表示なので、"ps ax"で全てのプロセスを表示します。opid,ppid,cmdはそれそれ、自身のpid、親プロセスのpid、コマンド名を出力するという意味です。その中からgrep で "nginx" を含む行だけを抽出します。ここまでの途中で出力してみるとこんな感じですね。
デーモンとして動くプロセスは、親プロセスのpidは1(init)になります。nginxでは、worker process は master processを親に持つため、pidが1なのはmaster processのみです。あと grep コマンド自身も実行中のシェルを親に持つため、pidが1のものを抜き出せば、それがmaster processになります。
とうことで、2列目に出力された親プロセスが1の行について、1列目のpidの値を出力するようawkで指定すればOKです。
参考になれば幸いです。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
nginx は master process というプロセスの小プロセスとして worker process を複数持ち、実際の処理はworker processが担当し、全体の起動・終了などのworker process の管理はmaster processが担当しています。
(参考→http://d.hatena.ne.jp/kiwanami/20111205/1323105070)
ということで、シェルスクリプトから終了させる場合には、master processにシグナルを送るのが正しい作法でしょう。pkillでnginx全体にシグナル送ってもいいかもしれませんが、せっかくmaster processがあるのに少々乱暴っぽいですし。
普通に起動・終了を制御するのであればngixの設定でpidfileを作成していると思うのでpidfileに記録されてるpidを見ればmaster processのpidを特定できますけれども、pidfileが何らかの理由で消えたか見つからないか読み取れない場合の処理を書いておきたいと思ったので、pidfile以外でmaster processのpidを見つける方法をとります。
ということで、以下のコマンドで取得できます。
ps ax opid,ppid,cmd | grep nginx | awk '$2 == "1" { print $1 }'
ps の a オプションは端末のある全てのプロセスの表示、xオプションは端末のないプロセスの表示なので、"ps ax"で全てのプロセスを表示します。opid,ppid,cmdはそれそれ、自身のpid、親プロセスのpid、コマンド名を出力するという意味です。その中からgrep で "nginx" を含む行だけを抽出します。ここまでの途中で出力してみるとこんな感じですね。
[root@host]# ps ax opid,ppid,cmd | grep nginx 5057 19498 grep nginx 6812 1 nginx: master process /usr/sbin/nginx 6813 6812 nginx: worker process 6814 6812 nginx: worker process 6815 6812 nginx: worker process 6816 6812 nginx: worker process
デーモンとして動くプロセスは、親プロセスのpidは1(init)になります。nginxでは、worker process は master processを親に持つため、pidが1なのはmaster processのみです。あと grep コマンド自身も実行中のシェルを親に持つため、pidが1のものを抜き出せば、それがmaster processになります。
とうことで、2列目に出力された親プロセスが1の行について、1列目のpidの値を出力するようawkで指定すればOKです。
[root@host]# ps ax opid,ppid,cmd | grep nginx | awk '$2 == "1" { print $1 }'
6812
参考になれば幸いです。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年1月25日土曜日
シェルスクリプトの全引数リスト $*, "$*", $@, "$@" の違い
シェルスクリプトで全引数リストをまとめて扱う変数は「$*」と「$@」がある。色々なサイトで解説があるが、自分の理解のため実際にスクリプトを書いて実験してみた。
用意したスクリプトはこちら:
test.sh
これを実行すると、次の結果が得られる。
ダブルクオーテーションで囲わない場合、$*と$@は同じで、元のダブルクオーテーションは無視される。
ダブルクオーテーションで囲った場合、"$*"は元のダブルクオーテーションを無視して全部を一括りに囲ったように動作するけど、"$@"はもとのダブルクオーテーションと同じように各引数をダブルクオーテーションで括ったものとして動作する。
言葉に書いて文章にしてみれば他のサイトに書いてあることと結局同じだけれど、こういうのは自分で実験してみると理解が深まるね。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。
用意したスクリプトはこちら:
test.sh
#!/bin/sh
print_arguments() {
echo "\$1: $1"
echo "\$2: $2"
echo "\$3: $3"
echo ""
}
test_pattern() {
echo '$*'
print_arguments $*
echo '"$*"'
print_arguments "$*"
echo '$@'
print_arguments $@
echo '"$@"'
print_arguments "$@"
}
echo 'arguments: "arg1 arg2" "arg3"'
echo ''
test_pattern "arg1 arg2" "arg3"
これを実行すると、次の結果が得られる。
[user@localhost]~% sh ./test.sh arguments: "arg1 arg2" "arg3" $* $1: arg1 $2: arg2 $3: arg3 "$*" $1: arg1 arg2 arg3 $2: $3: $@ $1: arg1 $2: arg2 $3: arg3 "$@" $1: arg1 arg2 $2: arg3 $3:
ダブルクオーテーションで囲わない場合、$*と$@は同じで、元のダブルクオーテーションは無視される。
ダブルクオーテーションで囲った場合、"$*"は元のダブルクオーテーションを無視して全部を一括りに囲ったように動作するけど、"$@"はもとのダブルクオーテーションと同じように各引数をダブルクオーテーションで括ったものとして動作する。
言葉に書いて文章にしてみれば他のサイトに書いてあることと結局同じだけれど、こういうのは自分で実験してみると理解が深まるね。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。
2014年1月23日木曜日
シェルスクリプトで trap にシグナル 0 を指定する意味
slapdの起動スクリプト(/etc/init.d/slapd)をカスタマイズしている時に、
trap コマンドにシグナル 0 を指定している記述を見つけた。
該当箇所はこんな感じになっている。
trapの意味は第1引数に実行するコマンド、第2引数以降は捕捉するシグナルだけれども、POSIXの定義には0という値のシグナルはない。では0を指定している意味はなんだろう?
と思って更に調べると見つかった。
http://shellscript.sunone.me/signal_and_trap.html
どうやら、シェルスクリプトが終了するときのEXITシグナルを補足することで、シェルスクリプト終了時に必ず実行される処理を指定できるということらしい。そして trap の第2引数に 0 と書くと EXIT と書いたのと同じ意味になるということだ。
上記リンク先の解説によると
なお、EXITの他にもDEBUG、ERR、RETURNがあるようだ。下記URLを参照。
http://fumiyas.github.io/2013/12/05/trap-exit.sh-advent-calendar.html
ちなみに冒頭で引用したスクリプトで trap の第1引数に "-" を指定している部分は捕捉を無効化するという意味。使い方的には try 〜 catch 構文を連想するなぁ。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。
trap コマンドにシグナル 0 を指定している記述を見つけた。
該当箇所はこんな感じになっている。
start_ldap() {
trap 'report_failure' 0
log_daemon_msg "Starting OpenLDAP" "slapd"
start_slapd
trap "-" 0
log_end_msg 0
}
trapの意味は第1引数に実行するコマンド、第2引数以降は捕捉するシグナルだけれども、POSIXの定義には0という値のシグナルはない。では0を指定している意味はなんだろう?
と思って更に調べると見つかった。
http://shellscript.sunone.me/signal_and_trap.html
どうやら、シェルスクリプトが終了するときのEXITシグナルを補足することで、シェルスクリプト終了時に必ず実行される処理を指定できるということらしい。そして trap の第2引数に 0 と書くと EXIT と書いたのと同じ意味になるということだ。
上記リンク先の解説によると
exit するルートが複数存在し、かつ終了時に共通の処理を行う必要があるシェルスクリプトにおいては非常に有効である。とのことだ。
なお、EXITの他にもDEBUG、ERR、RETURNがあるようだ。下記URLを参照。
http://fumiyas.github.io/2013/12/05/trap-exit.sh-advent-calendar.html
ちなみに冒頭で引用したスクリプトで trap の第1引数に "-" を指定している部分は捕捉を無効化するという意味。使い方的には try 〜 catch 構文を連想するなぁ。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。
登録:
投稿 (Atom)