dirname $0の問題点

スクリプト自体にシンボリックリンクをはって実行する場合、dirname $0をスクリプト内で実行するとスクリプト本体のディレクトリではなくシンボリックリンクのディレクトリが取得される。

スクリプト本体のディレクトリに設定ファイル等を配置していると、スクリプト本体のディレクトリパスを取得できないとファイルが読み込めない。

readlinkで解決

解決策はシンボリックリンクの実体を取得できるreadlinkと組み合わせてdirnameを使う。

$(dirname $(readlink -f $0))でスクリプトの実行パス$0の実体を取得してから、そのディレクトリを取得している。

スクリプトに組み込むときはスペースが含まれることを考慮して、以下のように念のためダブルクォーテーションで囲った方がいい。

cd "$(dirname "$(readlink -f "$0")")"
cat conf/list.cnf

manに記載されているreadlinkの主なオプションは以下の通り。

オプション 説明
-f, --canonicalize canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist
-e, --canonicalize-existing canonicalize by following every symlink in every component of the given name recursively, all components must exist
-m, --canonicalize-missing canonicalize by following every symlink in every component of the given name recursively, without requirements on components existence
-v, --verbose report error messages

シンボリックリンクの実体を取得するのにreadlink -fが使われることが一番多い。

readlinkの -f 以外のオプション

-fだと対象の親ディレクトリまで存在していれば、対象が存在していなくてもコマンドが成功する。-eだと対象までの親ディレクトリと対象自身が存在しなければならない。

スクリプト本体にシンボリックリンクがはられていて、スクリプト内部でreadlink -f $0する分には、スクリプトが既に実行されていることからスクリプトの存在は確実であり、readlink -e $0と差はない。

しかしスクリプトを書くのではなく日常でコマンドを打つときは、ファイルの存在チェックが一番厳しい-eのほうがいいと思う。よくある使用例として、今いるディレクトリにあるファイルのフルパスを取得するときにpwdの結果とファイル名を繋げるよりもreadlinkを使う。このときファイル名を打ち間違っても-fを使っていると間違いに気づかない。

スクリプト内部で使うときも、$0のように存在することが確実な場合はどうでもいいが、存在することが不確実なファイルやディレクトリに対してreadlinkを打つときは-eを使った方がいい。また-vで標準エラーにエラーを出力してくれるため、必要に応じてログに残す。