Practicalなシェルスクリプトの解説書 (α版)

[ Prev | Top | Next ]

ケース1:リロケータブルなスクリプト

スクリプトを別のディレクトリにコピーしたら動かなくなったり、 ログアウト→ログインしたらうまく動かなくなるという場合があります。

~(チルダ)で終ったファイルを削除する

"emacs"を使っていると増えるのが"名前が~(チルダ)で終るファイル"です。 ホームディレクトリの下から~で終るファイルを探し出して削除するスクリプトを作成します。

$ cd ~/
$ mkdir bin
$ vi bin/remove_~file.sh

"~/bin/remove_~file.sh"の内容は次のようにしました。

#!/bin/bash
find . -name "*~" -exec rm {} \; -printf "remove %f\n"

遭遇する問題

このスクリプトはホームディレクトリから実行する事で正常に稼働します。 次のようなコマンドを実行したらどうなるでしょうか。

$ cd /
$ ~/bin/remove_~file.sh

トップディレクトリ"/"から"find"コマンドを実行すると、時間と負荷がかかるだけになってしまいます。 こういう間違いはシェルのヒストリー機能で起こるかもしれません。

この他にも".logで終わるファイルをgzipで圧縮する"といったシェルスクリプトをcrontabに登録して、定時実行すると意図しないディレクトリのログファイルを圧縮することも考えられます。

考えられる対応

このスクリプトの修正方法を2つ考えてみます。 1つはホームディレクトリに"cd"する事で毎回決まった場所に移動するケース。 もう1つは"find"の引数にホームディレクトリを指定するケースです。

#!/bin/bash
cd ${HOME}
find . -name "*~" -exec rm {} \; -printf "remove %f\n"
#!/bin/bash
find ${HOME} -name "*~" -exec rm {} \; -printf "remove %f\n"

どちらも正解ですが、状況に応じて最適な方法は異なってきます。 通常は"cd"コマンドを使わずに解決する道をまずは探すべきでしょう。

カレントディレクトリ(==作業用ディレクトリ)を意識する

シェルに限らずプログラムを作成する場合には、カレントディレクトリを強く意識しなければなりません。 「ファイルを絶対パスで指定すればOKじゃない?」と考えるかもしれません。それも1つの解決策です。

しかし異常終了した時のcoredumpファイルや外部コマンドの出力先が、カレントディレクトリになる場合があります。 もしスクリプトをrootユーザーが"/"で実行している間に異常が発生すると、実行時にいたファイルシステムの使用率が100%に逹っする可能性があります。

スクリプトの中で "cd" コマンドを使っても良いのですが、その場合はスクリプトの先頭で実行し、相対パスを使う場合は "cd" 先のディレクトリを基点にするべきです。 coredumpファイルを作成する可能性のあるアプリケーションを起動する場合には、溢れても問題のないファイルシステムに "cd" し、スクリプト内部でのファイル指定には絶対パスを使うというハイブリッドな方法も検討するべきでしょう。

相対パスの使い方

設定ファイルの読み込みや、アプリケーションを特定のディレクトリの中に閉じ込めたい場合もあります。 例えばこのドキュメントに使用しているスクリプトは次のようなディレクトリ構造を持っています。

anakia-1.0/            .. コンテンツ作成用anakiaパッケージ
apache-ant-1.7.1/      .. 自動処理用antパッケージ
build.sh               .. antコマンドを実行するための環境変数を設定するwrapperスクリプト
xml/                   .. コンテンツ作成のファイル入力用ディレクトリ
html/                  .. 公開用htmlファイル出力先
prop/build.xml         .. ant用の設定ファイル
prop/shell_script.vsl  .. コンテンツ変換用のルールを設定したファイル

この構造を保つ限り、マシン上のどのディレクトリ以下に配置しても動く事が可能になります。 文書の生成には"build.sh"から"prop/build.xml"ファイルを読み込む必要があるため、 このスクリプトは次のような構造を持っています。

#!/bin/bash
...
BASEDIR="$(dirname $0)"
cd "$BASEDIR"
ant -f prop/build.xml"

ここでは "BASEDIR" 変数を cd "$BASEDIR" にしか使っていませんが、 他の処理でもこの情報を利用すると問題が起る場合があります。 例えば、次のような処理を考えてみます。

#!/bin/bash
BASEDIR="$(dirname $0)"
export PATH="$BASEDIR/apache-ant-1.7.1/bin:$PATH
...
cd "$BASEDIR"
...

$ ../build.sh のように起動したとすると、"$BASEDIR"は".."となり、PATH環境変数が差し示す場所は cd "$BASEDIR" の後では相対的に変化し、正しい場所ではなくなります。 使い方が難しいので通常は"cd"を使うべきではなく、"ant"コマンドの "-Dbasedir=$BASEDIR" オプションを使って解決する方法を探すべきなのですが、呼び出しているライブラリがどうしてもカレントディレクトリを参照するため止むを得ず"cd"コマンドを使っています。


Created: 2009-12-12, Last modified: 2010-03-19

2009,2010 © Yasuhiro ABE <yasu@yasundial.org>

Valid XHTML + RDFa RDFa it (RDF/XML)!

正当なCSSです!

Creative Commons License www.yasundial.org by Yasuhiro ABE is licensed under a Creative Commons Attribution 2.1 Japan License. Permissions beyond the scope of this license may be available at http://www.yasundial.org/info/license.html.