2011/12/14
cvs や svn のリビジョン番号
プログラムを作成する際、
特にまだ未完成で頻繁に修正とコンパイルを繰り返している間は、
どのリビジョンのプログラムを実行しているのか知りたい事はよく有る。
そんな時に、例えば実行時オプションとして -v を指定すると
cvs や svn のリビジョン番号とコミットした日時が表示されると便利だ。
しかし、多くの場合において -v オプションを解析し実行するだろう
main 関数が含まれるファイルが最終修正されているとは限らない。
従って main 関数の含まれるファイル中に
static char *id = "$Id$";などと定義しておいて commit 時に $Id$ を展開したとしても、 必ずしも最新のリビジョンにはならない場合が多い。
そこで -v オプションのハンドラ部分では
static char *id = "revision: " REVISION ", " COMMITED ;と記述としておいて make 時に自動で REVISION と COMMITED を全てのソース/ヘッダファイル中の cvs や svn の $Id$ キーワードから取得して設定する方法を検討してみる。
build 時に必ず実行する必要がある処理なので Makefile に記述して、 make 中で自動的に実行してしまうのが楽。
:
# 変数定義
PROGRAM = myprog
OBJECTS = sub.o pthread.o common.o util.o
HEADER = myprog.h const.h config.h
# 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンを取得する
REVISION = $(shell awk '/\$$Id/{ if($$4 ~ /[[:digit:]]/) \
if(max < $$4) max = $$4 } END{ print max }' \
$(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c)
# 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンのコミット日時を取得する
COMMITED = $(shell awk '/\$$Id.*'$(REVISION)'/{ \
print $$5 " " $$6; exit }' \
$(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c)
# コンパイルオプション(REVISIONとCOMMITEDを定義)
CFLAGS = -g -Wall -Wextra -Werror -Wno-unused-parameter -I.-O2 \
-DREVISION=\""$(REVISION)"\" -DCOMMITED=\""$(COMMITED)"\"
:
:
# ターゲット(デフォルトターゲットは touch と $(PROGRAM)に依存
all: touch $(PROGRAM)
# $(PROGRAM).o ファイルの削除($(PROGRAM).o ファイルは必ずコンパイルする)
touch:
rm $(@).o
$(PROGRAM): $(OBJECTS)
$(CC) $(CFLAGS) -o $(@) $^
$(OBJECTS): $(HEADER)
:
これでどのファイルを commit した場合でも
必ず最新のリビジョンとコミット日付が -v で表示される。
当初ターゲット touch は touch $(PROGRAM).c としていたが、
とある方よりの指摘で rm $(PROGRAM).o に変更した。
ご指摘感謝です > N.S. さま
2011/11/26
shell での while ループにおける問題
シェルスクリプトでコマンドの出力をループ処理する場合は
パイプ | を使用するのが一般的な手法だと思うが、
パイプ | を使うとループが別なプロセスとして実行されるので
ループ内部で設定したシェル変数がループ外部で参照できないという問題があり、
(存在するとすれば) shell script 業界では割と FAQ 的な問題だ。
何が問題かと言って posix で定義されていないから
シェルの実装に依存しているのが一番の問題だったりするのだろう。
この問題に対しては、シェルビルトインの exec (1) を利用して
ディスクリプタを複製して while ループの入力を標準入力にしてしまうのが
一番汎用的で柔軟性のある対応だと思うのだが、
シェルを愛する面々は
皆同じ様な苦労をしているのだ
と改めて認識した。
当初は一時ファイルを作成する方法を考えついたのだが、
自分的にはなるべく一時ファイルを使いたくなかったので
ファイルから入力する部分をバッククォートによるコマンド実行に置き換え等、
色々と試行錯誤した結果ヒアドキュメントを使う方法に落ち着いた。
# これでは正常に動作しなかった
exec 3<&0 0<`${command}`
while read line
:
勿論一時ファイルを作成しても良いのだが、 ヒアドキュメントを利用する事で一時ファイルが不要になる分だけ 処理的に美しいかなと思う (完全に好みの問題)。
1#!/bin/sh 2# コマンドの出力をバッククォートしてヒアドキュメントとして使用する 3 4command="/path/to/command" 5count=0 6 7# exec 3<&0: 標準入力 (FD0) を FD3 に複製 8# exec 0<<EOF: ヒアドキュメントを標準入力として使用 9exec 3<&0 0<<EOF 10`${command}` 11EOF 12 13while read line 14do 15 : 16 count=`expr ${count} + 1` 17done 18 19# exec 0<&3: FD3 に複製した標準入力 (FD0) を復帰 20# exec 3<&-: FD3 をクローズ</i> 21exec 0<&3 3<&- 22 23echo ${count}
もう一点は子プロセスで親プロセスの変数値を変更する方法。
こちらはプロセスが別になってしまうとファイルを使うしかないので、
同じプロセス内で関数呼び出しとして対応する方法が汎用的ではないだろうか。
1#!/bin/sh 2# eval を利用して呼び元の変数を設定する 3 4default="デフォルト値" 5 6sub() 7{ 8 9 local _val 10 11 : 12 _val="設定したい値" 13 14 eval "${1}='${_val:-${default}}'" 15 16} 17 18value="元の値" 19 20sub value 21 22echo ${value}
ちなみに上のリンク元にある parent.sh の中の処理で
${tmp_file} の内容を for ループで eval しているが、
これはシェルビルトインの . もしくは source を利用して
. ${tmp_file} としても同一の結果が得られるので楽だと思う。
1#!/bin/sh 2 3g1="aa" 4g2="bb" 5 6tmp_file=`mktemp /tmp/ps.XXXXXX` 7export tmp_file 8 9./child.sh # child.sh で、上で設定した変数 g1, g2 の値を変更する 10 11# for v in `cat $tmp_file`; do 12# eval $v 13# done 14# ${tmp_file} の内容は name='value' 形式が保証されているので 15# ファイル自体を `.' コマンドで読み込むだけで 16# 行毎に eval を実行しなくてもシェル変数に代入された値が利用できる 17 18if [ -f ${tmp_file} ] 19then 20 . ${fmp_file} 21 rm -f ${tmp_file} 22fi 23 24echo $g1 #=> cc に変更された 25echo $g2 #=> dd に変更されたブログに直接コメントしたかったのだが、 はてなのアカウントが無いので申し訳ないがここで意見させて頂く。
2011/11/16
メモリリークチェッカー
c で daemon 動作するサーバを作っていると
malloc (3) などで動的に確保したメモリの free (3) 忘れによる
いわゆるメモリリークを防ぐ事が非常に重要になってくる。
この様なメモリリークを防止するために役立つ
様々なツールが世の中には沢山存在しており
それぞれ非常に有用ではあるのだが、
いざ利用しようと思うと割と面倒な作業が発生したり、
自分の求めている機能に対して明らかにオーバースペックだったりするので、
自分の用途に合わせて簡単に使える『俺様ツール』を作ってみた。
動作原理は簡単で malloc (3) や free (3) したアドレスを
ログに出力して、
後からログを解析してメモリの確保と解放が対になっていない部分を
抽出するだけである。
そのためにまずは元となるソースコードを修正して、
malloc (3) や free (3) を独自に作成した wrapper 関数に置換えて
確保、解放したアドレスをログに出力する機能を組込む必要があるのだが、
malloc (3) などはエラー処理を含めて共通化するために
あらかじめ wrapper 関数化されている場合が多いと思うので、
この部分はフォーマットを決めるだけで割りと簡単に追加できると思う。
今回は syslog (3) を利用して malloc (3)、strdup (3)、
free (3) で確保/解放するアドレスを出力した。
1/* 2 * malloc() の wrapper 3 */ 4void *mymalloc(size_t size) 5{ 6 7 void *p; 8 9 if(!(p = malloc(size))) 10 /* エラー処理 */; 11 syslog(LOG_DEBUG, "malloc: %p\n", p); 12 13 return(p); 14 15} 16 17/* 18 * strdup() の wrapper 19 */ 20char *mystrdup(const char *s) 21{ 22 23 char *p = NULL; 24 25 if(s){ 26 if((p = strdup(s))) 27 syslog(LOG_DEBUG, "strdup: %p\n", p); 28 else 29 /* エラー処理 */; 30 } 31 32 return(p); 33 34} 35 36/* 37 * free() の wrapper 38 */ 39void myfree(void *p) 40{ 41 42 if(p){ 43 free(p); 44 syslog(LOG_DEBUG, "free: %p\n", p); 45 } 46 47}
次に出力されたログの解析処理であるが、
確保したメモリのアドレスをキーとしたハッシュテーブルを利用したいので
入力行の解析やハッシュテーブルが簡単に利用できる言語として
今回はコマンドライン版の php (1) を利用してみた。
もちろん awk (1) を駆使したり perl (1) を利用しても問題ない。
1<?php 2 /* 3 * Copyright (c) 2011 Mitzyuki IMAIZUMI, All rights reserved. 4 * 5 * $Id: memcheck.php 1677 2011-10-12 06:58:03Z mitz $ 6 */ 7 8 /* 冗長指定 */ 9 if($argv[1] == "-v"){ 10 $verbose = 1; 11 array_shift($argv); 12 } 13 14 if($fp = fopen($argv[1], "r")){ 15 $line = 0; 16 while($buf = fgets($fp, 1024)){ 17 $buf = ltrim($buf); 18 $line++; 19 /* 20 * malloc か strdup か free を含む行の場合 21 * コマンドを配列 $p[0] に、アドレスを配列 $p[1] に格納する 22 */ 23 if(preg_match("/^(malloc|strdup|free):\s(.*)/", $buf, $p)) 24 /* 解放処理 */ 25 if($p[1] == "free") 26 /* 確保済みテーブルに解放アドレスが存在する場合 */ 27 if($alloc[$p[2]]){ 28 if($verbose) 29 printf("%s: % 8d -> % 8d\n", $p[2], $alloc[$p[2]], $line); 30 $alloc[$p[2]] = 0; 31 } 32 33 /* 確保済みテーブルに解放アドレスが存在しない場合 */ 34 else 35 printf("%s: unknown free at % 8d\n", $p[2], $line); 36 else 37 /* アドレスをキーとしたハッシュテーブルに行番号を格納 */ 38 $alloc[$p[2]] = $line; 39 40 } 41 fclose($fp); 42 43 foreach($alloc as $k => $v) 44 if($v) 45 printf("%s: NOT free (% 8d)\n", $k, $v); 46 } 47?>このスクリプトを実行する事により malloc(3) もしくは strdup(3) で確保したメモリが解放されていない場合、 もしくは確保していないメモリを解放した場合が簡単に発見可能である。
これらは自分が必要とする機能のみを簡単に実装したものであり、 例えば解放していない事を意図しているメモリまで警告されてしまう等、 汎用的に使える事を目指したツールでは勿論ない。 ただ、日頃発生しうる面倒な作業がちょっとの工夫で 多少なりとも楽になるだろう例の一つとして公開してみた次第である。
2011/11/28 追記
awk (1) 版の方が汎用的なので簡単に作成してみた。 処理内容は上記 php (1) 版と同じである。1#!/bin/sh 2 3if [ "${1}" = "-v" ] 4then 5 verbose=true 6 shift 7fi 8 9awk ' 10 /^(malloc|strdup)/{ 11 alloc[$4] = NR; 12 } 13 /^free/{ 14 if(alloc[$4]){ 15 if("'${verbose:-false}'" == "true") 16 printf("% 5d %s: % 5d\n", NR, $4, $alloc[$4]); 17 alloc[$4] = 0; 18 } 19 else 20 printf("% 5d %s: unknown free.\n", NR, $4); 21 } 22 END{ 23 for(i in alloc) 24 if(alloc[i]) 25 printf("% 5d %s: NOT free.\n", alloc[i], i); 26 } 27' ${1} | sort ${2}
2011/10/18
電源関係の小物たち
いつも鞄に入れて持ち歩いている電源達関係の小物
- Apple 純正の AC アダプタ 普段から MacBookAir を持ち歩いているので、 Apple 純正の AC アダプタは必須 (IBM のバンドでまとめてあるのは愛嬌です)。
- USB 電源アダプタ 携帯電話や BlueTooth 機器など USB から充電できる機器が増えているので、 100V コンセントから USB の 5V を出力するアダプタも何かと便利。
- HyperMac 特許の関係で入手が不可になった HyperMac とそのケーブル。 これは MacBookAir からは通常の電源に見えるので、 純正のエアラインアダプタとは異なり充電もできる優れもの。 このおかげでお世辞にも長いとは言えない MacBookAir の駆動時間が 飛躍的に向上出来るのでモバイルには必須とも言えるアイテム。
- Griffin の USB Mini-Cableset。
Apple も特許が大切なのは判るんだけど、 こういう素晴らしい商品はライセンスするとか対応を考えて欲しいと思う。
2011/07/07
パイプコマンド中の終了ステータス取得
シェルスクリプトでは頻繁に複数のコマンドをパイプで連結して使用するが、
パイプの途中のコマンドの終了ステータスは通常では参照できない。
そこでリダイレクトを利用してパイプの途中のコマンドの
終了ステータスを取得する方法を考えてみる。
1exec 3>&1 2ret1=`{ { command1; echo $? 1>&3; } | command2; } 3>&1` 3ret2=$?exec を利用してあらかじめ FD3 を利用可能にしておき、 command1 の終了ステータスを FD3 に出力している。
command2 の実行後に FD3 の出力を FD1(標準出力) に変更しているので、 ret1 は標準出力から command1 の終了ステータスが取得できる。 command2 の終了ステータスはそのまま $? を参照して ret2 に格納される。
2011/07/04
awk (1) 小技
先日参加した勉強会で awk (1) 関連の発表があったので、 ちょっとした小技を紹介してみる。
- system() を利用する際の注意
-
awk の中で外部コマンドを実行する場合は
組み込み関数 system() が利用できるが、
この関数を利用して外部コマンドを実行すると
入出力 stream がオープンされるので、
ファイルディスクリプタが消費されてしまう。
従って awk 内で何度も system() を実行すると、 オープンできるディスクリプタの上限を超過してしまい、 awk の実行が中断されてしまう可能性がある。
その場合 close() を利用して system() がオープンした stream を閉じれば良いのだが、 awk の system() では stream を明示的に扱わない。 そこで system() で実行したコマンドを close() の引数で指定する事で stream を閉じる事ができる。1awk '{ 2 : 3 # コマンドを生成 4 command = sprintf("%s %s %s", command, arg1, arg2); 5 # コマンドを実行して結果を取得 6 buf = system(command); 7 # stream をclose 8 close(command); 9 : 10}'
- 関数内ローカルな変数の使用
-
nawk は function を利用する事で
内部関数を作成する事ができるが、
関数内でローカルな変数を明示的に定義する事ができない。
そこで関数宣言の仮引数部に変数を定義する事で、
関数内ローカルの変数として利用できる。
1awk '{ 2 : 3 # 関数 foo の定義 4 # num: ループ数 5 # 以下ローカル変数 6 # i: ループウンタ 7 function foo(num, i) 8 { 9 # i はローカル変数扱いなので呼出元の i は影響を受けない 10 for(i=0; i<1num; i++) 11 : 12 } 13 : 14 foo(num); 15 : 16 }'
但し関数呼び出し時と関数定義時に引数の数が等しくないので、 後に混乱しないためにコメントで明記するなどの処置は必要。
2011/07/01
Keyboards
現在職場で愛用中のキーボード達。
改めて見ると個人所有のキーボードが沢山あって驚いた。
- PFU Happy Hacking Keyboard PD-KB02
-
PFU Happy Hacking Keyboard PD-KB02Happy Hacking Keyboard としては2代目の製品かな? いちばん古くから愛用しているキーボード。
メンプレンスイッチのキータッチと シリンドリカル・カーブドスカルプチャの構成は 今でもいちばん気に入っていて、 FreeBSD 端末に接続されている。 プログラムを書くときは、このキーボードと FreeBSD と vi が必須。 - PFU Happy Hacking Keyboard Professional 2 PD-KB400WN
-
PFU Happy Hacking Keyboard Professional 2 PD-KB400WNUSB 接続タイプの新世代 Happy Hacking Keyboard。
静電容量無接点方式で押下圧が 45g なので、 キータッチは PD-KB02 に比べると若干軽く感じるが、 打鍵音が若干「カチャカチャ」気味なのが気になる。 シリンドリカル・ステップスカルプチャなので、 手を自然に置いた時の感じが PD-KB02 とは若干異なり 微妙な違和感を感じる。
- Apple Wireless Keyboard MB167LL/A
-
Apple Wireless Keyboard MB167LL/A薄型 Bluetooth 接続の Apple Wireless Keyboard で、 AA セルを 3 本利用する初代タイプ。
酸化皮膜処理のアルミニウムボディは非常に美しいのだが、 パンタグラフタイプなのでキータッチが軽すぎ、 しかもストロークが短いので打鍵感が頼りなく感じてしまい、 キー入力時に底打ちしてしまうのが微妙に使いづらい感じ。 以前は 27インチの iMac のメインキーボードとして活躍してた。 現在は自宅の TV に接続されている Mac MINI のキーボード。 - Lenovo ThinkPad USB TrackPoint Keyboard 55Y9003
-
Lenovo ThinkPad USB TrackPoint Keyboard 55Y9003かつての IBM Space Saver Keyboard の流れをくむ トラックポイントが搭載された薄型のキーボード。
こちらもパンタグラフタイプなのでキータッチが軽く、 ストロークも短めなので本家 IBM Space Saver Keyboard ほどは打鍵感が良くない。 - Apple USB Keyboard
-
Apple USB KeyboardUSB 接続の Apple 社純正フルキーボード。
メンプレンスイッチの打鍵感は素晴らしく、 シリンドリカル・カーブドスカルプチャなので違和感を感じない。
テンキーが付いているフルキーボードは邪魔なのだが、 Macintosh 独特の特殊キー(コマンドキー)をクリックしながら 電源をオンにするといった特殊な用途のために USB 接続の純正キーボードが必要な場合がまれにあるので 手元に置いてある。 - Fujitsu FKB8745-T101
-
Fujitsu FKB8745-T101一部では割と有名な富士通高見澤製のキーボード。
メンプレンスイッチのキータッチと シリンドリカル・カーブドスカルプチャというのは PD-KB02 と同じ構成だ。
そもそも PD-KB02 も富士通高見澤製なので 打鍵感なども殆ど変わらない。 PD-KB02 にファンクションキーと カーソルキーを付けた様なものだと思う。
Windows マシンを操作する場合はカーソルキーやファンクションキーが あった方が楽なので手元に置いてあったのだが、 今は手元に Windows マシンがなくなってしまったので、 サーバルームのコンソール用に利用している (良いキーボードなので若干勿体ないな)。 - PFU Happy Hacking Keyboard Type-S PD-KB400WNS
-
PFU Happy Hacking Keyboard Type-S PD-KB400WNS前述の Happy Hacking Keyboard Professional の進化版。
従来のモデルに比較するとキータッチが格段に良くなっている。 カチャカチャ音が完全に消されていて 静かで心地よい音になっているので、 静かなオフィスでも気にせずにタイピングに集中できるし 何よりも打鍵音に安っぽさがなくなって非常に良い感じ。
しかも各キーの軸が安定していてブレる感じがないので キー入力時に全く不快感を感じなくなった。Type-S という名称だけど全然別モノな感じでとても良い。 現在は iMac に接続されているが、 今の所 PD-KB02 と同じ位気に入った。
問題は高価な事かな。
2011/06/15
php の proc_open() を利用した openssl コマンドの実行
php ネタ
openssl (1) で暗号化されたファイルを復号化する処理を
php で実装する必要があった。
pear を探せばその手のモジュールは多分あるだろうと思ったが、
標準で含まれないモジュールに依存したくなかったので
外部コマンドの openssl を利用する方法を試してみた。
php では入力、もしくは出力のみの外部コマンド実行は
popen() を利用できるが、
入出力双方向が必要なので proc_open() を利用する。
最初は proc_open() した openssl の入力側パイプに
暗号化されたデータを全て出力 (fwrite()) した後で
出力側パイプから復号化されたデータを読み出し
(fread()) ていたのだが、
入力データがある程度のサイズを越えると
openssl はデータ読込みの途中でデータを出力しないと
読込み (もしくは処理) をブロックしてしまう様なので、
fwrite() の後に fread() を実行する様に修正した。
この時、当初は stream_select() を利用してパイプの出力側からの
入力可否を監視する様にしたのだが、
stream_set_bloking() を利用してパイプの出力側を
非ブロックモードにする事で、
パイプから読込めない場合でも fread() が即リターンするので
stream_select() の呼出しによりコードが煩雑になる事が避けられた。
1<?php 2 /* 3 * Copyright (c) 2011 Mitzyuki IMAIZUMI. All rights reserved. 4 * 5 * $Id: decrypt.php 3 2011-06-16 16:15:48Z mitz $ 6 */ 7 8 define("OPENSSL", "openssl enc -d -des3 -pass pass:%s"); 9 define("BLOCSIZE", "4096"); 10 11 /* 12 * ファイルの復号化 13 * $1: ファイル名 14 * $2: サイズ 15 * $3: パスフレーズ 16 */ 17 function decrypt($file, $size, $pass) 18 { 19 20 $desc = array( 21 0 => array("pipe", "r"), /* stdin: pipe */ 22 1 => array("pipe", "w"), /* stdout: pipe */ 23 2 => array("file", "/dev/null", "w") /* stderr: /dev/null */ 24 ); 25 26 if(($fp = fopen($file, "r+"))){ 27 if($data = fread($fp, $size)){ 28 if(preg_match("/^Salted_/", $data)){ 29 /* 暗号化されている場合 */ 30 if($pp = proc_open(sprintf(OPENSSL, $pass), $desc, $pipe)){ 31 /* 32 * `openssl enc -d …' を実行する。 33 * 34 * ファイルの内容はすでに $data に格納されているので 35 * BLOCSIZE 単位で openssl の標準入力に出力する。 36 */ 37 38 stream_set_write_buffer($pipe[0], 0); 39 stream_set_blocking($pipe[1], 0); 40 41 $buf = ""; 42 43 while($size > 0){ 44 /* 45 * 1 ブロック出力 46 * substr() は開始位置に負の値を指定すると 47 * 文字列の終端を起点とした開始位置からの 48 * 部分文字列が取得できる。 49 */ 50 fwrite($pipe[0], substr($data, 0 - $size, BLOCSIZE)); 51 $size -= BLOCSIZE; 52 /* 53 * openssl からの読み出し処理 54 * 非ブロッキングなので、 55 * 読めない場合は即座に fread() から戻る。 56 * 57 $buf .= fread($pipe[1], BLOCSIZE); 58 } 59 fclose($pipe[0]); 60 61 while(!feof($pipe[1])) 62 $buf .= fread($pipe[1], BLOCSIZE); 63 fclose($pipe[1]); 64 65 proc_close($pp); 66 } 67 /* 68 * 復号化したデータの出力 69 * 入力ファイルを書き換える 70 */ 71 fseek($fp, 0); 72 ftruncate($fp, 0); 73 fwrite($fp, $buf); 74 } 75 } 76 /* 暗号化されていない場合はそのまま close() する */ 77 fclose($fp); 78 } 79 80 } 81 82?>
2011/06/16 追記
ブロック転送のロジックを整理して最適化した。データサイズ $size をループの制御変数とする事で 余計な変数や転送ブロック数の計算を削除し、 openssl への出力データの部分文字列切り出し処理で substr() を利用する際に負の値を指定して 開始位置を文字列後端からの位置で指定する様に変更した。
2011/05/24
シェル変数への情報セット
例えば wget (1) コマンドへ proxy サーバの情報を伝えるためには http_proxy シェル変数に URL を http://[ID[:pass@]]FQDN[:port] 形式でセットする必要があるが、 proxy 情報が以下の形式でファイル格納されていれば 1 行で簡単にシェル変数に設定できる。
proxy.example.com # プロキシサーバの FQDN 8080 # プロキシサーバのポート番号 user # プロキシサーバの認証 ID passwd # プロキシサーバの認証パスワード
1#!/bin/sh 2 3setproxy() 4{ 5 6 local _var 7 8 _var=${1} 9 10 if [ -f ${2} ] 11 then 12 set -- `sed 's/#.*//g' ${2}` 13 test -n "${1}" && eval "${_var}=\${1:+http_proxy=http://\${3:+\$3\${4:+:\${4}}@}\${1}\${2:+:\${2}}}" 14 fi 15 16} 17 18setproxy http_proxy ファイル名 19sh -c "${http_proxy} wget …" 20 :
2011/05/13
ディスク容量のチェックツール
随分と時間が開いてしまったが、友人の awk (1) の勉強支援の第 4 段。 ディスクの容量をチェックして警告メイルを送信するためのスクリプト。
df (1) の内容を簡単に解析して、
ルートパーティションの使用率が指定した値以上の場合は警告メイルを送信し
logger (1) を利用して syslog にも警告を出力する。
1#!/bin/sh 2# 3# All rights reserved, copyright (c) 2011, Mitzyuki IMAIZUMI 4# $Id: rdf,v 1.1 2011/05/13 17:38:09 mitz Exp $ 5# 6 7myname=${0##*/} 8tmpfile=${TMP:-/tmp}/${myname}.$$ 9logger=/usr/bin/logger 10df="LANG=C df -h" 11 12# 警告メイルの宛先 13to=root@example.com 14 15# 警告処理 16warning() 17{ 18 19 LANG=C 20 subject="##### [ ${1}: Disk Usage ] #####" 21 ${logger} "${subject}" 22 cat << EOF | mail ${to} -s "${subject}" 23 24${1} 25 26`date` 27 28`cat ${2}` 29EOF 30 31} 32 33# 終了時に一時ファイルを削除 34trap 'rm -r ${tmpfile}; exit' 0 1 2 3 9 13 15 35 36# メイン処理 37 38# tee(1) を使って df(1) の出力を awk(1) と一時ファイルの両方に出力 39${df} | tee ${tmpfile} | 40 awk '{ 41 # 最後のフィールドが '/' の場合 (ルートパーティション) 42 if($NF == "/") 43 # 最後の直前のフィールドが使用率なので閾値との比較結果をリターン 44 exit($(NF-1) < '${1:-70}') 45 }' && warning `hostname` ${tmpfile}

