どのディレクトリから実行されても動作する入れ子形式のシェルスクリプトを作る

対象プラットフォーム: FreeBSD 8.x
(下記手順を2014/05/01にFreeBSD 8.4-RELEASEで確認済み)


はじめに

入れ子形式のシェルスクリプト(別のシェルスクリプトを内部的に呼び出すシェルスクリプト)を実行する場合、親のシェルスクリプトの呼び出し方によっては、子のシェルスクリプトを上手く呼び出せないことがあります。

たとえば、以下のような親子関係のある2つのシェルスクリプトが同じディレクトリ「/home/work」に格納されているとします。

parent.sh
#!/bin/sh

./child.sh
child.sh
#!/bin/sh

echo 'Hello World!'

カレントディレクトリが「/home/work」である場合に、「parent.sh」を実行した場合は、下記の通りになります。

「/home/work」からの「parent.sh」の実行
$ pwd
/home/work

$ ./parent.sh
Hello World!

しかしながら、これをカレントディレクトリが「/home/work」以外の場合に実行すると、「parent.sh」から「child.sh」が正しく呼ばれません。
たとえば、カレントディレクトリが「/home」の場合に、「parent.sh」を下記のように実行すると、結果は下記のようになります。

「/home」からの「parent.sh」の実行
$ pwd
/home

$ ./work/parent.sh
./work/parent.sh: ./child.sh: not found

確かに、カレントディレクトリが「/home」のため、そこからの相対パスである「./child.sh」=「/home/child.sh」は見つからずエラーになるのは当然といえば当然ですが、シェルスクリプトを実行する際のカレントディレクトリを制限するのは、シェルスクリプトを実行する上で、何かと不便です。

このページでは、この問題を解決する方法について説明します。

シェルスクリプトが実行されているパスを知る「$0」と「dirname」

上記の問題を解決するには、実行されているシェルスクリプトを知る「$0」なる特別の引数と、「dirname」を利用します。
これらの動きを知るために、「/home/work」に以下のようなシェルスクリプトを作って保存してみます。

test.sh
#!/bin/sh

echo $0
echo `dirname $0`

このシェルスクリプトを同じディレクトリ「/home/work」から実行したときの結果は以下の通りです。

「/home/work」からの「test.sh」の実行
$ pwd
/home/work

$ ./test.sh
./test.sh
.

また、このシェルスクリプトを「/home」から実行したときの結果は以下の通りです。

「/home」からの「test.sh」の実行
$ pwd
/home

$ ./work/test.sh
./work/test.sh
./work

更に、このシェルスクリプトを「/」から実行したときの結果は以下の通りです。

「/」からの「test.sh」の実行
$ pwd
/

$ ./home/work/test.sh
./home/work/test.sh
./home/work

$ /home/work/test.sh
/home/work/test.sh
/home/work

このように、「$0」にはシェルスクリプトを実行する際に入力されたコマンドそのものを返し、「dirname $0」は「そのディレクトリへのパス」(相対パス/絶対パスを問わない)を返します。

親のシェルスクリプト「parent.sh」の変更

説明が長くなりましたが、一番最初の「parent.sh」および「child.sh」の例において、これらが同じディレクトリにあれば、「parent.sh」の中で取得する「dirname $0」は「child.sh」のあるディレクトリと同一になります。

「parent.sh」を以下のように変更してみてください。

parent.sh
#!/bin/sh

binary_path=`dirname $0`

${binary_path}/child.sh

このシェルスクリプト「parent.sh」を相対パス/絶対パスのどれで呼び出しても、且つカレントディレクトリが何処にあろうと、「child.sh」が正しく呼び出されます。

子のシェルスクリプトの中での「dirname $0」

尚、子のシェルスクリプトの中で「dirname $0」を呼び出すと、親のシェルスクリプトの中での「dirname $0」の値と同じものが取得できます。
つまり、子のシェルスクリプトが同一のディレクトリ内にある孫のシェルスクリプトを呼ぶ際にも、以下のようにすれば正しく呼び出すことが可能です。

child.sh
#!/bin/sh

binary_path=`dirname $0`

${binary_path}/grandchild.sh

変更履歴

2012/04/01

・「/home」からの「parent.sh」の実行の件で、parent.shの実行方法の記述が誤っていたのを修正。
ご指摘頂いた方、深く感謝です!


あなたの探し物は見つかりましたか?
まさにこれだ
参考になった
ちょっと違う
これじゃない

何かメッセージがあればお願いします

このメッセージを非公開にする

ご注意

・頂いたメッセージは管理者のチェックの後、公開されます。
・メッセージの公開を希望されない場合には、「このメッセージを非公開にする」にチェックを入れてください。
・管理者が不適切と判断したメッセージは公開しませんので、予めご了承ください。


まさにこれだ
1 (100%)

表示できるメッセージはありません。


目次に戻る
image