初出:SoftwareDesign 2003/9月号
第2特集 UNIXプログラミングツールをマスターせよ
◆シェルスクリプト実践テクニック〜bash編
くわむら じゅん
juk@yokohama.email.ne.jp
・bashシェルスクリプトの特徴,利点
「プログラミングはスクリプトに始まりスクリプトに終
る」とは誰か言ってそう(?)ですが、最近はJAVAがコン
パイラ言語のほうの人気をうんと高めてくれてるようで
す。しかしながら、JAVA というのがこれまでのプログ
ラミング言語の範疇にあてはまるのかどうかは、JAVAの
無い時代のコンパイラ言語でプログラムを書いてきた者
としては疑問になります。少なくとも筆者の知っている
プログラミング言語はいくつかの文の使いまわしを覚え
るとプログラムを書くことができたはずなのですが、こ
と、JAVA になるとさっぱり手が付けられせん。JAVAで
アプリケーションを作っている人に聞くと、プログラム
を書くというよりも、必要な機能をインターネットで拾っ
てカット&ペーストということらしく、あたかも、パズ
ルを組み立ているかのようです。まさに工場で言うとこ
ろのアッセンブリ工程のようなものだと思いました。こ
のあたりが、単なる言語を越えたものである結縁なので
しょう。これほどまでに信者を集めたJAVAにはほとんど
のものが揃っているということでもあると思います。
JAVAのために用意されたJCSPというクラスは、コンパク
トな通信カーネルで、スレッドなど使わずに安全な並列
分散処理ができてしまうそうです。通信をするためのソ
ケットプログラミングを例にとると、C言語では少なく
とも6、7ステップ(ソケットを決めて、初期化して、
プロトコルを決めて、ポートを決めて、バインドして、
データをやりとりする)かかってしまう通信のためのソ
ケットプログラミングも、JAVAで書くと2、3ステップ
(ソケットを作成し、入力ストリーム決め、出力ストリー
ム決め、データをやりとりする)で済んでしまいます。
ところが、JCSPではソケットさえ意識しないで通信がで
きてしまうというので驚きです。とはいえ、ネットワー
クがコンピューティングの基本要素となった昨今では、
とてもありえる話です。
JCSP=> http://members.jcom.home.ne.jp/1355/CSP_Java.htm
シェルプログラミングでもそういう通信機能が使えれば
便利なのになあと思うこともあります。確かに、TCLや
PERLなどのスクリプト系プログラミング言語ではそうし
た機能拡張も盛んに行なわれていますが、bashを含めた
シェル系では言語の拡張機能にして使うよりも、既に作
られたプログラムを利用することのほうが多く、複数の
プログラムを組み合わせ実行してバッチ処理を行なう、
シェルプログラミングのほうが主体といえましょう。プ
ログラミングにシェルを選択するかどうかはこのあたり
の見極めが大事です。もちろん、bashにおいても拡張機
能の追加は可能で、PgBashのようにシェルから直接に
PostgreSQL DBMSを操作するような便利な使い方もあり
ます。
PgBash=> http://www.psn.co.jp/PostgreSQL/pgbash/index-j.html
ちょっとしたファイルの整形やテキスト処理を複数のファ
イルに対して行なう必要がある場合はどうでしょうか?
日常的な業務においては単純な処理の繰返しはよくあり
ます。異なる文字コードを使っているコンピュータ間で
のファイルのやりとり、扱うデータの書式が少し異なる
アプリケーション間でのデータやりとり。ファイルをま
とめて印刷したり、ログの整理をしたり。手で行なって
も簡単にできるけど、同じ作業の繰返しをするとなると、
おっくうだし、単純作業を続けるとミスをしがちになり
ます。そこで、複数の操作をひとつのファイルにして間
違えのないように、作業を省力化して効率をあげたいと
きにシェルプログラミングを覚えていれば、とても役に
たちます。
C言語を使った経験のある人ならCシェルを使うほうが比
較的簡単ですが、OSまわりのシェルスクリプトはBシェル
(Bournシェル)系のほうが多いようです(かつては、
CONVEX のように Perlを使っているOSもありました)。
LinuxではBシェル(sh)の代わりにbashが使われてい
ます。bashは sh との互換性を維持しながら拡張されて
いますので、シグナルをトラップできることはいうまで
もなく、変数の照合演算子や数値演算など、プログラミ
ング言語としても便利な機能が多く追加されています。
それでは、bashでプログラミングをするための機能を
見てみましょう。
・条件分岐,変数,引数などの解説
============
変数について
============
bashの変数はダラー記号($)で始まる変数名のついた文
字列です。変数の定義はダラー記号が付かない変数名に
続けて等号記号(=)、それに続けて値を記述します。変
数名の先頭にダラー記号を付けて参照することができま
す。正確には、${変数名} のように変数名を左右のカー
リーブレースで囲むのですが、左右のブレース記号は省
略可能です。次の例は varname という名前の変数に
"newvalue" という文字列を与え、それを表示していま
す(行頭の'$ 'はbashのコマンドラインプロンプトを示
します)。
--
$ varname=newvalue
$ echo $varname
newvalue
--
一般的に大文字で定義される環境変数は export するこ
とによってサブプロセスから参照することができます。
bash ではexport文で変数への代入も同時にできるよう
になっています。
--
$ export LNAG ja_JP.eucJP
--
言い方を代えると、現行シェルでexportされた変数は、
そのシェルから起動されたプログラム(サブプロセス)
からも参照可能になります。
プログラムを実行する際にスペースやタブ記号で区切
られた引数として様々なオプションをわたすことがある
と思います。bashのサブプロセスはこれら引数を位置パ
ラメータの変数として扱うことができます。引数の位置
パラメータは指定された順番に、左から順番に1,2,3...
と数字の名前が付けられ、bashスクリプトの中では
$1, $2, $3...のように変数として参照が可能です。な
お、$0 は起動された時に指定された自分自信のパス名
となります。$# は引数の個数になります。また、$* と
$@ は引数すべての文字列となります。(いずれも、0番
目の位置パラメータの値は含まれません)。IFS変数が指
定された場合は、これらの変数は指定された区切り文字
で分割された、複数の引数として内部的には扱われます。
例えば、次のようなスクリプトのファイルをつくり実行
してみると、
test1.sh
--
IFS=,
echo "$# arguments"
function countarguments
{
echo "$# arg."
}
countarguments $*
--
--
$ . test1.sh a1 a2 a3
3 arg.
3 arg.
$ test1.sh a1,a2,a3
1 arg.
3 arg.
--
カンマ区切りの文字列を渡した場合も、関数内部では3
つの引数として扱われています。
============
関数について
============
関数がでてきたのでついでにその説明をしておきます。
bash の関数はつぎの2種類の方法で定義することが
できます。
その1)
function 関数名
{
関数の処理内容
}
その2)
関数名 ()
{
関数の処理内容
}
いずれも呼び出された関数内での引数はシェルと同様の
$1, $2, $3...のような位置パラメータ変数として参照
可能です。これらの位置パラメータ変数は、$0を除くと、
関数を呼び出すのものとは異なりますので注意してくだ
さい。たとえば、次のようなスクリプトをつくり、
test2.sh
--
function myfunc
{
echo "func=>\$0:$0, \$1:$1, \$2:$2, \$3:$3."
}
myfunc
myfunc a b c
echo "main=>\$0:$0, \$1:$1, \$2:$2, \$3:$3."
--
引数を指定して実行してみると、
--
$ . test2.sh 1 2 3
func=>$0:test2.sh, $1:, $2:, $3:.
func=>$0:test2.sh, $1:a, $2:b, $3:c.
main=>$0:test2.sh, $1:1, $2:2, $3:3.
--
メインのシェルと関数の中では位置パラメータ変数は関
係のないことがわかります。
では、他の変数はどうでしょうか。例えば、次のような
スクリプトをつくり、
test3.sh
--
function myfunc1
{
var=$(($var+1))
echo "func=>$var"
}
var=1
echo "main=>$var"
myfunc1
echo "main=>$var"
--
実行してみると、
--
$ . test3.sh
main=>1
func=>2
main=>2
--
という具合に、変数は引数にしなくても関数に渡り、関
数内で変更された変数値は関数を呼び出したメインのシェ
ルに戻っても残っています。
========
数値演算
========
bashは次の表に示す数値演算子による整数演算を行なう
ことができます。
演算子 演算
------------+----------------------------
+ 加算
- 減算
* 乗算
/ 除算(小数点以下切捨て)
% 剰余
<< 左へビットシフト
>> 右へビットシフト
& 論理積
| 論理和
~ 論理否定
! 論理否定
^ 排他的論理和
先ほどの test3.sh では var=$(($var+1)) という代入
文がでてきました。この行は、 $var+1 を数値として計
算してvarへ代入をしています。このように $(( と ))
とで囲まれたブロックは数式として処理されます。この
数値演算式では変数に $ を付ける必要はありません。
bash の数値演算処理で注意しなくてはならないことは、
整数を処理対象としていることです。例えば、次のよう
なスクリプトをつくり、
test4.sh
--
function myfunc2
{
val=$1
var=$((val+1))
echo "func=>$var"
}
myfunc2 3
myfunc2 3.3
--
実行すると小数の場合はエラーになります。
--
$ . test4.sh
func=>4
bash: 3.3: syntax error in expression (error token is ".3")
--
数値演算では上記の数値演算子の他に以下の関係演算子
(比較演算子と論理演算子)による演算もできます。
演算子 意味
------------+---------------
< より小さい
> より大きい
<= 以下
>= 以上
== 等しい
!= 等しくない
&& かつ
|| または
bashによる数値演算は、このほかに let 文を使うこと
もできます。 let 文で使う変数を declare 文で整数値
として宣言しておく必要があります。例えば、次のスク
リプトのように記述できます。
test5.sh
--
declare -i a=1 b=2
let c=$a+$b
echo "$a + $b = $c"
--
もし代入される変数も declare で整数に宣言しておけ
ば、この let は省略することも可能です。例えば、次
のスクリプトのように記述できます。
test6.sh
--
declare -i a=1 b=2
declare -i c
c=$a+$b
echo "$a + $b = $c"
--
もちろん、Bournシェルのように expr コマンドによる
数値演算も可能です。この場合は、バッククォート(`)
で expr コマンド行を囲んで実行させ、その結果を変数
に代入するオーソドックスなシェルのコマンド文で記述
します。例えば、次のスクリプトのように記述できます。
test7.sh
--
a=1
b=2
c=`expr $a + $b`
echo "$a + $b = $c"
--
==========
文字列演算
==========
bashの変数はブレース構文の中で文字列演算子による値
(文字列)の操作が可能です。変数にデフォルト値を設
定したり、文字列を部分的に取り出したりすることがで
きます。
文字列演算子 処理
----------------------+-----------------------------------
${変数:-デフォルト} 変数がなければデフォルトを返す
${変数:=デフォルト} 変数がなければデフォルトを設定する
パターン照合演算子 処理
-----------------------+---------------------------------------------------------------------
${変数#パターン} 変数値にパターンがあれば最初の最短パターンより前を返す
${変数##パターン} 変数値にパターンがあれば最後の最長パターンより後を返す
${変数%パターン} 変数値にパターンがあれば最初の最短パターンより後を返す
${変数%%パターン} 変数値にパターンがあれば最後の最長パターンより後を返す
${変数/パターン/文字列} 変数値にパターンがあれば最初の最長パターンを文字列に置換する
${変数//パターン/文字列} 変数値にパターンがあればすべての最長パターンを文字列に置換する
以下にパターン照合の例を示します。
例) filepath=/opt/apache/conf/httpd.conf.template に対するパターン照合
${filepath} /opt/apache/conf/httpd.conf.template
${filepath##/*/} httpd.conf.template
${filepath#/*/} apache/conf/httpd.conf.template
${filepath%.*} /opt/apache/conf/httpd.conf
${filepath%%.*} /opt/apache/conf/httpd
================
シグナルトラップ
================
Bournシェルゆかりの機能が出てきたついでに、シグナ
ルのトラップについてもふれておきます。trapコマンド
はシグナルを受けとった時に実行するディスパッチコマ
ンドを指定できます。例えば、実行中に途中でキャンセ
ルしたときにメッセージを出して終了させたい場合は次
のように書きます。フォアグラウンドで実行中のプログ
ラムのキャンセルは、コントロールキーとCキーを同時
に押してINT(割り込み)シグナルを送ることで(^Cと
記述します)、通常できます。
test8.sh
--
#!/bin/bash
trap "echo 'INT signal recieved';exit" INT
while true; do
ps ax | grep $0 | grep -v grep
sleep 3
done
--
このプログラムの実行はファイルに実行許可を与えて
(chmod +x test8.sh)、直接実行してください。そうし
なければ、親のbashプロセスから抜けることができなく
なり、実行中の端末をkillし(終了させ)なくてはなら
なくなります。
--
$ ./test8.sh
5133 pts/0 S 0:00 /bin/bash ./test8.sh
^C
INT signal recieved
--
もちろん、trapの2つめの引数にはファンクションも指
定することができますので、次のように記述することも
可能です。INTシグナルの場合は、exitで終了しなけれ
ば、イベントの表示だけを行なうことも可能です。
test9.sh
--
#!/bin/bash
function dispatch
{
echo 'INT signal recieved'
}
trap dispatch INT
while ( true ) do
ps ax | grep $0 | grep -v grep
sleep 3
done
--
この場合に実行端末からプログラムを中止するにはコン
トロールキーとZキーを同時に押して(^Zと記述します)
一旦停止して、バックグラウンドプロセスにしてから終
了させることができます。
$ ./test9.sh
5359 pts/0 S 0:00 /bin/bash ./test9.sh
INT signal received
5359 pts/0 S 0:00 /bin/bash ./test9.sh
INT signal received
5359 pts/0 S 0:00 /bin/bash ./test9.sh
5359 pts/0 S 0:00 /bin/bash ./test9.sh
^Z
[1]+ Stopped ./test9.sh
$ bg
[1]+ ./test9.sh &
$ kill %
[1]+ 終了しました ./test9.sh
シグナルの名前については man kill を実行して確認して
ください。
========
制御構造
========
すでに出てきたのですが、while 文は bash の繰返し処
理の制御を行ないます。構文は次のように while に続
けて条件を記述し、それに続く行には do と done の行
にはさんで処理を記述します。
--
while 条件
do
処理内容
done
--
たとえば、次のスクリプトは引数からパラメータを得る
ためのものです。
test10a.sh
--
#!/bin/bash
while [ "$#" -gt 0 ]
do
case $1 in
-h|--help|-\?)
echo "This is help message."
exit 0
;;
-n|--max)
max=$2
shift
;;
*)
echo "$0: invalid option or parameter: $1" 1>&2
exit 1
;;
esac
shift
done
echo "max=$max"
--
shift命令は、$2の値を$1へ、$3の値を$2へというよう
に引数を一つずつシフトします。いくつかオプションを
与えて実行してみると次のようになります。
$ ./test10a.sh miss
test10a.sh: invalid option or parameter: miss
$ ./test10a.sh --help
This is help message.
$ ./test10a.sh -n 2
max=2
bashでは ; を実行命令と解するので、次のように書く
こともできます。
--
while 条件; do
処理内容
done
--
while文では条件が真の間は処理が行なわれるのですが、
これに対して、until文では条件が偽の間は処理が行な
われます。
--
until コマンド; do
処理内容
done
--
基本的なLinux(UNIX)のコマンドは処理が正常に終了す
ると 0 の状態コードを返し、すなわち偽となるので、
until はコマンドが正常終了するまで、コマンドの実行
を繰り返したいときに便利です。
もうひとつの繰返し処理のために for 文があります。
for 文ではリストから項目をひとつずつ取り出して指定
した一時変数に格納して処理を行なうことができます。
for 一時変数名 in リスト
do
処理内容
done
例えば、次のスクリプトはそのディレクトリにある
拡張子が .txt のファイルをすべて EUC に変換し
て、もとのファイル名に .euc を付け加えた名前の
ファイルに出力します。
test10.sh
--
#!/bin/bash
for file in *; do
nkf -e $file > $file.euc
done
--
もう少し、いろいろと条件を付けて処理を行ないたい場
合には if 文による判定を加えることができます。if
ブロックの基本的な形は、
if 条件
then
処理内容
fi
ですが、then と fi の間には、 elif と else のブロッ
クを加えることもできます。if 文の条件のところには
コマンドを記述してその終了時のステータス値を真偽の
判定条件にすることができます。条件には [ と ] とで
括った条件式、もしくは、ステータスを返すコマンドが
使えます。
test11.sh
--
#!/bin/bash
if [ -f /etc/passwd ]; then
if grep "^www:" /etc/passwd; then
echo "www is existed"
else
echo "www is not existed"
fi
fi
--
この条件の [ -f /etc/passwd ] は、/etc/passwd とい
う通常ファイルが存在するかどうかを調べます。この
'-f' はファイル属性演算子と言い、ほかの演算子も
含めて以下の属性を調べられます。
演算子 調べる属性
-d ディレクトリ
-e 存在する
-f 通常ファイル
-r 読み込み可能
-s 空ではない
-w 書き込み可能
-x 実行可能
-O 所有者に一致
-G グループIDが一致
また、以下の演算子でファイルの更新時刻の比較を行な
える。
演算子
-nt 左側のファイルは右側のファイルより新しい
-ot 左側のファイルは右側のファイルより古い
このスクリプトは次のように書くこともできます。
test12.sh
--
#!/bin/bash
if [ -f /etc/passwd ] && grep "^www:" /etc/passwd; then
echo "www is existed"
else
echo "www is not existed"
fi
--
2つの if文の条件を論理演算子の && を使って併せて
います。論理演算子には || もあります。&& は論理積
(かつ)を表し、|| は論理和(または)を表します。
また、条件の前に ! を置くと否定になります。論理演
算子にはこれらの他に、[ と ] とで囲まれた条件式の
中で利用可能な論理積 -a と論理和 -o もあります。条
件式の中でのグループ化は \(バックスラッシュ)でエ
スケープした \( と \) とで括ることで表せます。
条件式の中で、以下の比較演算子を使うと文字列の比較を
おこないます。
文字列演算子 意味
-------------+----------------------------------------
= 左右の文字列が同じ
!= 左右の文字列は異なる
< 左側の文字列のほうが辞書では先に現れる
> 左側の文字列のほうが辞書では後に現れる
このため、数値として比較したい場合は以下の演算子を
使う必要があります。
数値演算子 意味
-------------+----------------------------------------
-eq 等しい
-ne 等しくない
-lt より小さい
-gt より大きい
-le 以下
-ge 以上
なお、さきほどのスクリプトを実行すると、www がパス
ワードファイルに存在するときに、次のように grep の
出力も表示されてしまいます。
--
$ ./test12.sh
www:x:10005:101::/home/www:/opt/pgsql/bin/pgbash
www is existed
--
プログラムの出力を表示したくない場合は、/dev/null
へ出力をリダイレクトします。エラー出力も表示したく
ない場合はエラー出力ユニット番号の 2 を標準出力ユ
ニット番号の 1 へリダイレクトする記述も加えます。
たとえば、次のようにします。
test12.sh
--
#!/bin/bash
if [ -f /etc/passwd ] && grep "^www:" /etc/passwd 1> /dev/null 2>&1; then
echo "www is existed"
else
echo "www is not existed"
fi
--
これで、grep コマンドの出力は表示されなくなりました。
--
$ ./test12.sh
www is existed
--
======
デバグ
======
筆者はデバッガも使わずひたすら出力文(echo)でデバグ
をする始末です。あえて、使う簡単な方法はbashの実行
オプションの -x を使って、実行している行を表示させ
ることです。とはいえ、bashにもbashdbという専用デバッ
ガはあるそうですので、本格的にプログラムを書く際は
使ってみてください。
==================
サンプルプログラム
==================
解説したいくつかの機能を使ったサンプルスプログラム
としてPlamo Linux上で PostgreSQLパッケージを作成す
るためのスクリプトを紹介します。build.pgsql73 とい
うスクリプトがそれです。このスクリプトは
./custom/pgsql/ ディレクトリに、インストーラ
doinst.sh73 とインストーラがインストールのときにメ
モリサイズの設定に使うスクリプト
(get_memory_parameters)が用意されていることを前提
にしています。
build.pgsql73
========================================================================
1 #!/bin/bash
2 # PostgreSQL
3 # BuildScript for Plamo Linux 3.x
4 # by Jun Kuwamura on 2003-07-10
5 #
6 umask 022
7 CWD=`pwd`
8 WORK=$CWD/work
9 rm -rf $WORK/*
10 mkdir -p $WORK
11 export LANG=C
12 export CFLAGS="-O2"
13 export PATH=$PATH:/usr/X11R6/bin
14
15 SRC_URI="ftp://ftp.postgresql.org/pub/source/v7.3.3/postgresql-7.3.3.tar.gz"
16 FAQ_URI="http://www.postgresql.jp/subcommittee/jpugdoc/faq-japanese.txt"
17
18 SRC_FILE=${SRC_URI##*/}
19 FAQ_FILE=${FAQ_URI##*/}
20 MSG_FILE=${MSG_URI##*/}
21 SRC_DIR=$CWD/archive/pgsql
22 mkdir -p $SRC_DIR
23 PAC_NAME=${SRC_FILE%%.tar.gz}
24 PAC_DIR=$CWD/package/Database
25 mkdir -p $PAC_DIR
26 CUSTOM_DIR=$CWD/custom/pgsql
27 mkdir -p $CUSTOM_DIR
28
29 DOC_DIR=/usr/doc/$PAC_NAME
30 PREFIX_DIR=opt
31
32 REL=1
33 SYS=i386
34 SYS_NAME="${SYS}-${REL}"
35
36 if [ ! -f $SRC_DIR/$SRC_FILE ]; then
37 (cd $SRC_DIR; wget $SRC_URI);
38 fi
39 (cd $CUSTOM_DIR; wget -N $FAQ_URI);
40
41 if [ -d /$PREFIX_DIR/pgsql ] ; then
42 mv /$PREFIX_DIR/pgsql /$PREFIX_DIR/pgsql.bak
43 fi
44 rm -f /$PREFIX_DIR/pgsql
45
46 # making main package
47 tar xvfz $SRC_DIR/$SRC_FILE
48 cd $PAC_NAME
49 ./configure --prefix=/$PREFIX_DIR/pgsql \
50 --enable-nls --enable-syslog --with-openssl=/usr
51 make
52 make install
53
54 # install documents
55 mkdir -p $WORK/$DOC_DIR
56 cp -a COPYRIGHT HISTORY INSTALL README register.txt config.status \
57 doc/FAQ* doc/README* doc/TODO* doc/KNOWN_BUGS doc/MISSING_FEATURES \
58 doc/bug.template $WORK/$DOC_DIR
59 cp -f $CUSTOM_DIR/$FAQ_FILE $WORK/$DOC_DIR/FAQ_japanese
60 chown -R root.root $WORK/$DOC_DIR
61 chmod -R a+rX,go-w $WORK/$DOC_DIR
62
63 # packing
64 cd $WORK
65 tar cfp - /$PREFIX_DIR/pgsql | tar xvfp -
66 mv opt/pgsql opt/$PAC_NAME
67
68 cp opt/$PAC_NAME/share/pg_hba.conf.sample opt/$PAC_NAME/share/pg_hba.conf.template
69 cat >>opt/$PAC_NAME/share/pg_hba.conf.sample<<__EOCF
70 # Allow login user access to the database which name is the same
71 # as the username
72 #local sameuser trust
73
74 # Allow local network user whos password in the admin file
75 #host all 192.168.2.0 255.255.255.128 md5 admin
76 # Allow local network and password created with pg_passwd passwd
77 # command under data directory if you uncomment the followings.
78 #hostssl all 192.168.2.0 255.255.255.128 password passwd
79 #hostssl all 0.0.0.0 0.0.0.0 krb5
80 __EOCF
81
82 mkdir -p install
83 sed -e "s/PGSQLDIR/$PAC_NAME/g" -e "s/PREFIXDIR/$PREFIX_DIR/g" $CUSTOM_DIR/doinst.sh73 > install/doinst.sh
84
85 mkdir -p opt/$PAC_NAME/apps/utils
86 cp $CUSTOM_DIR/get_memory_parameters opt/$PAC_NAME/apps/utils/
87 chmod +x opt/$PAC_NAME/apps/utils/get_memory_parameters
88
89 # make package
90 echo "n
91 " | installpkg -m $PAC_NAME
92 mv ${PAC_NAME}.tgz $PAC_DIR/${PAC_NAME}-${SYS_NAME}.tgz
93 echo "$PAC_DIR/${PAC_NAME}-${SYS_NAME}.tgz ... done"
94 cd $CWD
========================================================================
まず、6〜13行目で作業環境の初期化をしています。
17,18行めでインターネットからダウロードするソース
ファイルを指定しています。実際にダウンロードを試み
るのは34〜38行目です。21〜32行目は主にスクリプト内
で利用する変数の設定をしています。URIの文字列から
ファイル名だけを取り出したり、さらに、拡張子を取り、
部分的に抜き出してパッケージ名にしたりしています。
作業中にインストールする場所の確保を41〜44でします。
そして、いよいよアーカイブを展開してプログラムのメ
イクとインストールを47〜52行目あたりでやってます。
55〜61行目ではドキュメント類をパッケージング用の作
業ディレクトリへインストールします。64行目からパッ
ケージにするファイルを作業ディレクトリに準備します。
68行目からは設定ファイルをヒヤドキュメントにして作っ
ています。83〜87行目はインストーラの準備です。
最後に90行目からinstallpkgプログラムを使って
パッケージを作成しています。
参考までに、./custom/pgsql/ ディレクトリに用意する
インストーラのソースを示します。
doinst.sh73
========================================================================
1 #!/bin/sh
2 # This should be done at initial booting after installation.
3 #
4 export LANG=C
5 if [ -r /tmp/SeTT_PX ]; then
6 ROOT="`cat /tmp/SeTT_PX`"
7 fi
8
9 cd $ROOT/
10
11 if [ -d $ROOT/PREFIXDIR/pgsql ] ; then
12 mv $ROOT/PREFIXDIR/pgsql $ROOT/PREFIXDIR/pgsql.bak
13 fi
14 rm -f $ROOT/PREFIXDIR/pgsql
15 ln -sf $ROOT/PREFIXDIR/PGSQLDIR $ROOT/PREFIXDIR/pgsql
16
17 (cd $ROOT/usr/doc/PGSQLDIR && \
18 rm -f doc && \
19 ln -s $ROOT/PREFIXDIR/PGSQLDIR/doc doc )
20
21 #
22 # Create Environment
23 #
24 PG_ROOT=$ROOT/PREFIXDIR/PGSQLDIR; export PG_ROOT
25
26 # Add user and group
27 groupadd pgsql
28 useradd -g pgsql -d $ROOT/PREFIXDIR/pgsql -c "Postgres Admin." -s /bin/tcsh postgres
29 chown -R postgres.pgsql $ROOT/PREFIXDIR/PGSQLDIR
30
31 # This is required for Kerberos authentication
32 if ! fgrep "postgres" /etc/services 1> /dev/null 2>&1; then
33 echo 'postgres 5432/tcp # Reserve for PostgreSQL' >> /etc/services
34 else
35 echo "Entry postgres is already in /etc/services."
36 fi
37
38 if [ ! -f /etc/rc.d/init.d/postgresql ]; then
39 cp /PREFIXDIR/pgsql/etc/init.d/postgresql /etc/rc.d/init.d/
40 chmod +x /etc/rc.d/init.d/postgresql
41 fi
42
43 if ! fgrep "/PREFIXDIR/pgsql/lib" $ROOT/etc/ld.so.conf 1> /dev/null 2>&1; then
44 echo "/PREFIXDIR/pgsql/lib" >> $ROOT/etc/ld.so.conf
45 if [ "$ROOT" = "" ] ; then
46 /sbin/ldconfig
47 fi
48 else
49 echo "Entry /PREFIXDIR/pgsql/lib is already in $ROOT/etc/ld.so.conf."
50 fi
51
52 # add LOG_LOCAL1 for syslogd report
53 if ! fgrep "/var/log/pgsqlog" $ROOT/etc/syslog.conf 1> /dev/null 2>&1; then
54 echo "local1.* /var/log/pgsqlog" >> $ROOT/etc/syslog.conf
55 if [ ! -f $ROOT/var/log/pgsqlog ]; then
56 touch $ROOT/var/log/pgsqlog
57 fi
58 if [ "$ROOT" = "" ] ; then
59 kill -HUP `pidof syslogd`
60 fi
61 else
62 echo "Entry pgsqlog is already in /etc/syslog.conf."
63 fi
64 if [ -f $ROOT/etc/logrotate.conf ]; then
65 if ! fgrep "/var/log/pgsqlog" $ROOT/etc/logrotate.conf 1> /dev/null 2>&1; then
66 cat >> $ROOT/etc/logrotate.conf <<__LOGROT
67 # postgresql's syslog output rotation
68 /var/log/pgsqlog {
69 rotate 5
70 postrotate
71 /bin/killall -HUP syslogd
72 endscript
73 }
74 __LOGROT
75 fi
76 fi
77
78 if ! fgrep "Add by Plamo Linux 3.x" $ROOT/PREFIXDIR/PGSQLDIR/share/postgresql.conf.sample 1> /dev/null 2>&1; then
79 cat >> $ROOT/PREFIXDIR/PGSQLDIR/share/postgresql.conf.sample <<__PGCONF1
80
81 #
82 # Add by Plamo Linux 3.x installer
83 syslog = 2
84 syslog_facility = 'LOCAL1'
85 syslog_ident = 'postgres'
86 tcpip_socket = true
87 ssl = true
88
89 __PGCONF1
90
91 $ROOT/PREFIXDIR/PGSQLDIR/apps/utils/get_memory_parameters -n 64 \
92 >>$ROOT/PREFIXDIR/PGSQLDIR/share/postgresql.conf.sample
93 fi
94
95 # set access control file(open for your network)
96 NADDR=`ifconfig eth0 | fgrep inet | sed -e 's/.*addr:\(.*\) .*Bcast:\(.*\) .*Mask:\(.*\)/\1/'`
97 NMASK=`ifconfig eth0 | fgrep inet | sed -e 's/.*addr:\(.*\) .*Bcast:\(.*\) .*Mask:\(.*\)/\3/'`
98 if [ "$NADDR" != "" ] ; then
99 sed -e "s/192.168.2.0/$NADDR/" -e "s/255.255.255.128/$NMASK/" \
100 $ROOT/PREFIXDIR/PGSQLDIR/share/pg_hba.conf.template > $ROOT/PREFIXDIR/PGSQLDIR/share/pg_hba.conf.sample
101 fi
102
103 # Add enviroment variables to rc files
104 # for tcsh
105 if [ ! -f $ROOT/PREFIXDIR/PGSQLDIR/.cshrc ]; then
106 cat > $ROOT/PREFIXDIR/PGSQLDIR/.cshrc <<__CSHRC
107 setenv PG_DIR /PREFIXDIR/PGSQLDIR
108 setenv PGDATA ${PG_DIR}/data
109 setenv PGLIB ${PG_DIR}/lib
110 #setenv PGPORT 5432
111 #setenv PGHOST localhost
112 if (! $?LD_LIBRARY_PATH) then
113 setenv LD_LIBRARY_PATH $PG_DIR/lib:/usr/X11R6/lib
114 else
115 setenv LD_LIBRARY_PATH $PG_DIR/lib:${LD_LIBRARY_PATH}
116 endif
117 if (! $?MANPATH) then
118 setenv MANPATH ${PG_DIR}/man:/usr/man:/usr/share/man:/usr/X11R6/man
119 else
120 setenv MANPATH ${PG_DIR}/man:${MANPATH}
121 endif
122 set path=( ${PG_DIR}/bin $path )
123 __CSHRC
124 fi
125 # for bash
126 if [ ! -f $ROOT/PREFIXDIR/PGSQLDIR/.bashrc ]; then
127 cat > $ROOT/PREFIXDIR/PGSQLDIR/.bashrc <<__BASHRC
128 PGROOT=/PREFIXDIR/PGSQLDIR
129 export PGDATA=$PGROOT/data
130 export PGLIB=$PGROOT/lib
131 #export PGHOST=localhost
132 #export PGPORT=5432
133
134 export PATH=$PGROOT/bin:$PATH
135 export LD_LIBRARY_PATH="$PGROOT/lib:${LD_LIBRARY_PATH-/usr/X11R6/lib}"
136 export MANPATH="$PGROOT/man:${MANPATH-/usr/share/man:/usr/X11R6/man}"
137 __BASHRC
138 fi
139
140 #
141 # ( DONOT CHANGE THE NEXT ONE LINE )
142 # Add PostgreSQL Entry to Apache document index.
143 #
144 if [ -f $ROOT/PREFIXDIR/apache/htdocs/contents.list ]; then
145 if ! fgrep "PGSQLDIR" $ROOT/PREFIXDIR/apache/htdocs/contents.list 1> /dev/null 2>&1; then
146 cat >> $ROOT/PREFIXDIR/apache/htdocs/contents.list <
149 PGSQLDIR
150 (
151 http://www.jp.postgresql.org/
154 |
155 http://www.postgresql.jp/
158 )
159 (Object Relational Database Management System)
160 documentation
163
164 EOL
165 fi
166 if [ ! -f $ROOT/PREFIXDIR/apache/htdocs/PGSQLDIR ]; then
167 ln -sf $ROOT/usr/doc/PGSQLDIR $ROOT/PREFIXDIR/apache/htdocs/PGSQLDIR
168 fi
169 else
170 awk '/Add PostgreSQL Entry/,EOF' $ROOT/install/doinst.sh >> /tmp/add_apache_test.sh
171 fi
get_memory_parameters
========================================================================
1 #!/bin/bash -f
2 #
3 # Calculate memory parameters for PostgreSQL-7.1 |!あ|on Linux.
4 #
5 # This program calculate sample values of "sort_mem" and "shared_buffers"
6 # in $PGDATA/postgresql.conf for your Linux environment according to your
7 # Linux configurated parameters.
8 #
9 # Author: Jun Kuwamura
10 # Creation: 2001-04-11
11 #
12 bug_report_to="juk@yokohama.email.ne.jp"
13 #
14 CMDNAME=`basename $0`
15
16 # set default and parameter
17 ##max_connections = 32 # 1-1024
18 max_connections=32
19 denominator=2
20
21 help="\
22 $CMDNAME: Calculate memory parameters for PostgreSQL-7.1
23
24 Usage:
25 $CMDNAME [-n max_connections]
26 where;
27 max_connections: number of maximum connections
28 [default $max_connections]
29
30 Report bugs to <$bug_report_to>."
31 advice="\
32 Try '$CMDNAME --help' for more information."
33
34
35 while [ "$#" -gt 0 ]
36 do
37 case $1 in
38 -h|--help|-\?)
39 echo "$help"
40 exit 0
41 ;;
42 -n|--max_connections)
43 max_connections=$2
44 shift
45 ;;
46 *)
47 echo "$CMDNAME: invalid option or parameter: $1" 1>&2
48 echo "$advice" 1>&2
49 exit 1
50 ;;
51 esac
52 shift
53 done
54
55 if [ $max_connections -lt 1 -o $max_connections -gt 1024 ]; then
56 echo "max_connections must be between 1 and 1024"
57 exit
58 fi
59
60 ##sort_mem = 512 # [k bytes]
61 #sort_mem = 1024 # core_mem / 10 / max_connections
62 #
63 core_mem=`free | grep Mem: | awk '{print $2}'`
64 sort_mem=`expr $core_mem / $denominator / $max_connections`
65
66
67 ##shared_buffers = 2*max_connections # min 16 [8k bytes]
68 #shared_buffers = 2048 # [8k bytes]
69 shm_max_bytes=`cat /proc/sys/kernel/shmmax`
70 shm_max=`expr $shm_max_bytes / 1024`
71 shared_buffers=`expr $shm_max / $denominator / 8`
72
73 # check minimum number of buffers
74 shared_buffers_min=`expr $max_connections \* 2`
75 if [ $shared_buffers -lt $shared_buffers_min ]; then
76 shared_buffers=$shared_buffers_min
77 fi
78
79 echo "max_connections = $max_connections"
80 echo "sort_mem = $sort_mem"
81 echo "shared_buffers = $shared_buffers"
========================================================================
参考文献
「入門bash第2版」, ISBN4-900900-78-8
サンプルプログラムのダウンロード