シェルスクリプトでのシンボリックリンクの解決方法

Unix系システムでシンボリックリンク(またはシンリンク)を扱うのは、特にシンリンクが指し示す実際のパスを知りたいときには難しいことがあります。シェルスクリプトを書いていて、中間のシンリンクを解決した後にターゲットのフルパスを特定する必要がある場合、正しい場所にいます。このブログ投稿では、これを効率良く達成する方法を説明します。

問題の理解

シンボリックリンクfooが別のディレクトリbarを指し示している状況を想像してみてください。cd foopwdのようなコマンドを実行しても、シェルは基盤となる構造を明確に示さないため、ターゲットへのフルパスを導き出すのが難しいことがあります。

例えば、次のような出力が得られます。

ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar

以下のコマンドを実行すると:

cd foo
pwd

出力は単に/Users/greg/tmp/fooとなり、fooが指し示す実際のパスである/Users/greg/tmp/barを知りたい場合、あまり役に立ちません。

解決策

幸いなことに、シェルスクリプトにおいてシンリンクを解決するのに役立つ組み込みコマンドがあります。Cプログラミングに深入りする必要なく、次のように行うことができます。

pwdコマンドの使用

pwd -Pコマンドは、現在の作業ディレクトリを表示するために使用され、すべてのシンリンクを解決してターゲットの絶対パスを提供します。次のコマンドを実行すると:

pwd -P

シンリンクを解決した後のフルパスが返されます。

getcwdの使用

別の方法は、Cのunistd.hライブラリからgetcwd関数を利用することです。ただし、スクリプトを書いている場合は、シェルコマンドを利用することができます。getcwdpwd -Pと似た動作を持つことを知っておくと良いでしょう。

解決関数の構築

これらのコマンドを関数にラップして作業を楽にすることができます。以下はBashシェルでのシンプルなresolve関数の例です。

resolve() {
    # 提供されたパスを使用して解決
    local target=$1
    # `readlink -f`はシンリンクを辿って、フルパスを返します
    local full_path=$(readlink -f "$target")
    echo "$full_path"
}

使用例

次に、単に次のように関数を呼び出します:

resolve "foo"

これにより、次の出力が返されます:

/Users/greg/tmp/bar

このパターンを使えば、シェルスクリプト環境で遭遇した任意のシンリンクのフルパスにアクセスすることができます。

ボーナス: ユーザーホームパスの解決

ティルダ表記(例えば~username)を使ってホームディレクトリを解決したい場合、Bashはシェルスクリプト内でこれらのパスを自動的に展開するため、resolve関数と組み合わせてシームレスに利用できます。

結論

シェルスクリプトでシンボリックリンクを解決するのは、複雑である必要はありません。pwd -Pコマンドを利用するか、readlinkを使用するヘルパー関数のようなresolveを作成することで、ターゲットへのフルパスを効果的に決定できます。このスキルは、Unix系環境で作業する人には不可欠であり、スクリプトをより堅牢で情報量の多いものにします。

シンリンクを適切に管理することで、ディレクトリ構造のナビゲートや複雑なシェルスクリプトの作成時に時間を節約し、混乱を避けることができます。