シェルスクリプトについて ( ports 編 )

シェルスクリプトが面白い。
FreeBSD を使っているのにプログラミングも出来ないハナたれな Cocelo です。
今日はシェルスクリプトについてメモろうかと思います。


何故シェルスクリプトに興味を持ったかというと、サーバを管理する者としてはシェルスクリプトが必須と感じたからです。
・・・なんて偉い事言えたらいいんですが、実際は違います。


その理由というのが、下記のサイト様にある 499.status-pkgupdate というシェルスクリプトがどのように動いているのか気になりメモとして残そうと思ったからです。
id:cocelo:20060531:1149060832 でも紹介しましたが、その時はシェルスクリプトについて全く考えていなかったので、今回は portsnap をシェルスクリプトから使う事に絞って紹介します。
BSD にくびったけ - portsnap


また、このエントリーで解説しているのシェルスクリプトid:hiro-ueda 様が書かれたものです。
著作権id:hiro-ueda 様が保有しています。何か問題がありましたらコメントの方に突っ込んでください。


portsnap に関する情報をまとめて下さった id:hiro-ueda 様、シェルスクリプトを解説する際に参考にさせてドキュメントを作成した全ての方々に、この場を借りてお礼申し上げます。


さて、早速本題のシェルスクリプトですが、まずは下記のサイト様で基本的な事を頭に入れるのが良さそうです。


rhythm-cafe.com
bashで始めるシェルスクリプト基礎の基礎 (1/2):Windowsユーザーに教えるLinuxの常識(8) - @IT
http://cyberam.dip.jp/linux_command/shellscript/shellscript_main.html
http://www.ybi.co.jp/koike/src/BSH.htm


Google 先生にお伺いして、自分で分かりやすいと感じたサイト様をピックアップしてみました。
上記のサイト様の内、いずれかを参考にすれば ( もしくは並列して参考にすれば ) シェルスクリプトで出来る事のイメージが湧くと思います。
読み終えたら早速シェルスクリプトを読み解いていくことにしましょう。


まずは 8 行目から 12 行目の記述です。

if [ -r /etc/defaults/periodic.conf ]
then
    . /etc/defaults/periodic.conf
    source_periodic_confs
fi


/etc/defaults/periodic.conf に関係がありそうなので見てみみます。
最後の方に下記の様な記述がありました。

# Define source_periodic_confs, the mechanism used by /etc/periodic/*/*
# scripts to source defaults/periodic.conf overrides safely.


上記の記述を読むと 499.status-pkgupdate の 8 行目から 12 行目の記述は /etc/defaults/periodic.conf オーバーライドする為の記述らしいです。
periodic(8) 自体良く分かっていないで periodic(8) を使用する時に記述する『おまじない』として覚えておけばいいと思います。
# 間違っていたらご指摘ください。


次に 14 行目から 15 行目の記述です。

export PATH=$PATH:/usr/local/sbin:/usr/local/bin
export LANG=C


これは環境変数ロケールを通す為の記述です。
詳しくは下記のサイト様をご覧下さい。
環境変数:PATH: UNIX/Linuxの部屋
環境変数:LANG: UNIX/Linuxの部屋


次に 17 行目の記述です。

[ ! -x /usr/local/sbin/portsnap ] && exit 0


上記の記述は /usr/local/sbin/portsnap がある場合、 exit 値 0 を返すという意味です。
これは exit 値を返す事によってシェルスクリプトを実行し続けるかどうか判断する為の記述です。
詳しくは後述します。


2006/07/06 16:40 頃 追記:上記の解説は間違いでした。
また、後述にも間違いがある事が判明したのでそちらも書き直しています。詳しくは後述で。


上記のコマンドは正しくは、 /usr/local/sbin/portsnap が存在し、かつ実行可能でなければ、 exit 値 0 を返し、シェルスクリプトを終了とのことです。
id:otsune 様から下記のご指摘を承りました。

test(1)(man [)を見ると「! expression True if expression is false.」および「-x file True if file exists and is executable.」なので
「/usr/local/sbin/portsnapが存在し、かつ実行可能じゃなければ(&&なので)戻り値0でexitしてスクリプト終了」という意味です。


次に 19 行目から 20 行目の記述です。

echo
echo "Ports/packages update check:"


上記の記述は daily run output に表示される内容のものです。
これは更新するパッケージが存在する場合に表示されます。


次に 21 行目の記述です。

(portsnap cron && portsnap update) >/dev/null 2>&1


上記の記述は portsnap cron と portsnap update を実行して、エラーを含めてその内容を表示しないというものです。
パイプラインを使用して動作していますが、ここではパイプラインについては扱いません。
# また、ここで解説しているスクリプト/bin/sh で動いています。
# /bin/csh や /bin/tcsh では記述内容が違いますのでご注意ください。


パイプラインについて詳しく知りたい方は下記のサイト様をご覧ください。
http://web.sfc.keio.ac.jp/~masudako/class/computer/filter/pipeline.html

/dev/null については下記のサイト様を。
Unix/dev/nullの役割 - Pocketstudio.jp Linux Wiki


次に 23 行目から 31 行目の記述です。

if [ $? -eq 0 ]; then
	portsdb -u >/dev/null 2>&1
	portversion -vL=
	rc=0
else
	echo "Failure of portsnap(*ERROR*)"
	rc=1
fi
echo


上記の記述は 17 行目で解説した exit 値が 0 と等しい場合そのまま実行して、 0 以外の数値だった場合は『 Failure of portsnap(*ERROR*) 』を表示する、という内容のものです。
また、 21 行目の記述と同じく portsdb -u は内容が表示されません。 portversion は出力されます。


2006/07/06 16:40 頃 追記:上記の記述も間違いでした。
正しくは 21 行目の『 (portsnap cron && portsnap update) >/dev/null 2>&1 』が正常判断したかどうかを判定しています。
こちらも id:otsune 様からご指摘を承りました。
下記がその内容になります。

>if [ $? -eq 0 ]; then
は17行目ではなくて21行目の「(portsnap cron && portsnap update) >/dev/null 2>&1」が正常終了したかどうかを判定していることになります。


# rc=0 と rc=1 については後述します。


最後に 33 行目の記述です。

exit $rc

これは 17 行目の記述で解説した exit 値を返す為のものです。
通常シェルスクリプトは実行するコマンドが無ければそのまま終了しますが、 exit 値を返す事によって他のシェルスクリプトに組み込んだ時に正常終了したのか、エラーが出たのか分かります。
シェルスクリプトを自作する時は覚えておくといいかもしれません。


以上、自分用のメモとシェルスクリプトの勉強を兼ねて駆け足で解説してきました。
こういった管理の自動化というのはサーバ管理者にとって大切な事だと思います。
最後に有用なシェルスクリプトを作成してくださった id:hiro-ueda 様とドキュメントを作成された方々に感謝です。
# 後日 499.status-pkgupdate を元にシェルスクリプトを書くかもしれません。期待しないでください


2006/07/06 16:40 頃 追記
id:otsune 様、検討違いな解説にご指摘頂き、ありがとうございました。
この場を借りてお礼申し上げます。


余談ですが、こちらの記事は何度も書き直している内に見辛くなってしまったので別のエントリーでまとめるか、 Wiki を借りてきてまとめなおすかもしれません。
っていうか私の頭がこんがらがってきました。 :-)


2006/07/06 18:20 頃 追記
改訂版をポストしてみました。
少しだけ分かり易くなっていると思います。 ( 多分 )
改訂版 シェルスクリプトについて ( ports 編 ) - Cocelo Style