realpath / readlink 入門 - 絶対パスとシンボリックリンクを解決する

realpath / readlink 入門 - 絶対パスとシンボリックリンクを解決する

この記事で分かること

  • realpathreadlink違いと使い分け が分かる
  • シンボリックリンクや .. を含むパスを 絶対パスに解決 できる
  • -f / -e / -m存在チェック 3 段階 を理解できる
  • スクリプトで 事故らないパス解決 が書けるようになる

結論(先に要点)

  • パスを絶対パスにしたいrealpath path
  • シンボリックリンクの「中身」を見たいreadlink link
  • リンクも .. も全部たどって 1 本の実体パスにしたいrealpath path または readlink -f path
  • スクリプトで使うなら readlink(素)ではなく realpathreadlink -f を使う

前提(対象環境)

  • GNU coreutils(Ubuntu / Debian / RHEL 系など一般的な Linux)
  • realpath は coreutils 8.15 以降で標準同梱
  • 本記事の挙動は coreutils 9.4 で確認

realpath と readlink の違いは?

結論: readlink はシンボリックリンクの「中身(リンク先文字列)」を読むコマンド。realpath はパス全体を実体の絶対パスに解決するコマンド。readlink -f を付けると realpath とほぼ同じ働きになる。

両者は名前も用途も近いが、素の状態での役割が違う

観点 readlink(オプションなし) realpath(オプションなし)
主目的 リンク先の文字列を読む 実体の絶対パスを求める
対象がシンボリックリンク以外 何も出力せず失敗 そのまま絶対パスを出力
相対リンク先 格納された相対文字列のまま 解決して絶対パスにする
.. の解決 しない する

ポイントは 「素の readlink は symlink 専用」 という点。通常ファイルやディレクトリに対して素の readlink を使うと無言で失敗する(後述の落とし穴)。一方 realpath はどんなパスでも絶対パスを返す。

結論: 引数なしの readlink はリンク先文字列をそのまま表示する。-f を付けるとリンクを再帰的にたどり、最終的な絶対パスを返す。

リンクの「中身」を表示する(オプションなし)

readlink は、シンボリックリンクに格納されている文字列をそのまま表示する。多くのシステムで /binusr/bin への相対リンクになっている。

$ readlink /bin
usr/bin

格納値が相対パス(usr/bin)なので、出力も相対のまま。.. も解決されない。「リンクが何を指しているか」を確認したいときに使う。

-f / -e / -m で完全に解決する

-f--canonicalize)を付けると、途中のシンボリックリンクをすべてたどり、.. も処理した絶対パスを返す。

$ readlink -f /bin
/usr/bin

-f 系には存在チェックの強さが異なる 3 つのモードがある。

オプション 別名 存在要件
-f --canonicalize 最後の要素以外が存在すればよい
-e --canonicalize-existing すべての要素が存在しなければならない
-m --canonicalize-missing 存在不問(全部なくてもよい)
# 親は存在し、最後のファイルだけ無い → -f は成功、-e は失敗
$ readlink -f /etc/does-not-exist.conf
/etc/does-not-exist.conf

$ readlink -e /etc/does-not-exist.conf
# 何も出力せず exit 1

「これから作るファイルの最終パスを先に求めたい」なら -f-m、「実在を保証したい」なら -e を使う。

readlink でリンク先を表示したいとき、-n--no-newline)を付けると末尾の改行を抑制できる。コマンド置換 $(...) は末尾改行を自動で除くため通常は不要だが、printf で連結するときなどに役立つ。

realpath はどう使うのか?

結論: realpath path はシンボリックリンク・... をすべて解決した絶対パスを返す。デフォルトは「最後の要素以外が存在すればよい」挙動で、readlink -f と一致する。

基本: リンクと相対パスを 1 本の実体パスにする

$ realpath /bin
/usr/bin

シンボリックリンクでないパスでもそのまま絶対パス化できるのが readlink との大きな違い。

$ cd /var/log
$ realpath ../tmp
/var/tmp

...、重複スラッシュも正規化される。

存在チェックを制御する(-e / -m)

realpath の存在チェックは readlink と同じ 3 段階。

  • デフォルト: 最後の要素以外が存在すればよい(親までは実在が必要)
  • -e--canonicalize-existing): すべての要素が存在しなければならない
  • -m--canonicalize-missing): どの要素も存在しなくてよい
# 親ディレクトリが存在しない → デフォルトでも失敗
$ realpath /no-such-dir/file.txt
realpath: /no-such-dir/file.txt: No such file or directory

# -m なら存在しないパスでも正規化して返す
$ realpath -m /no-such-dir/file.txt
/no-such-dir/file.txt

--relative-to で相対パスに変換する

realpath は絶対パス化だけでなく、基準ディレクトリからの相対パスも計算できる。

$ realpath --relative-to=/home /home/alice/work/report.txt
alice/work/report.txt

スクリプトで「設定ディレクトリからの相対位置」を出したいときなどに便利。--relative-base=DIR を使うと、対象が DIR 配下のときだけ相対化し、外なら絶対パスのままにできる。

-s でリンクを展開しない

-s--strip / --no-symlinks)を付けると、シンボリックリンクを解決せず... だけを正規化する。リンク構造を保ったままパスを整えたいときに使う。

$ realpath -s /bin/../bin
/bin

よくある落とし穴は?

結論: 最大の罠は「素の readlink は通常ファイルに対して無言で失敗する」こと。スクリプトでパス解決するなら realpathreadlink -f を使う。

readlink(オプションなし)はシンボリックリンク以外に対して何も出力せず exit 1 で終わる。

$ readlink /etc/hostname
# 出力なし、exit 1(/etc/hostname は通常ファイル)

スクリプトでこう書くと、対象が symlink でないときに変数が空になり、思わぬ事故につながる。

# 危険: $f が symlink でないと p が空になる
p=$(readlink "$f")

# 安全: symlink でも通常ファイルでも絶対パスが入る
p=$(realpath "$f")

2. -f と -e の取り違え

「まだ存在しない出力先パスを解決したい」のに -e を使うと失敗する。これから作るパスは -f(最後の要素は無くてよい)か -m(全部無くてよい)を選ぶ。

3. 末尾スラッシュとシンボリックリンク

シンボリックリンクの末尾に / を付けると、リンク自身ではなくリンク先のディレクトリを指す。意図せず解決結果が変わることがあるため、リンクそのものを調べたいときは末尾 / を付けない。

使い分けの目安

  • リンクが指す「文字列」を 1 段だけ見たい → readlink link
  • 実体の絶対パスが欲しい(スクリプト含む)→ realpath pathreadlink -f path
  • 実在を保証したい → -e
  • これから作るパスを先に求めたい → -f-m

スクリプトでの実践例

結論: スクリプト冒頭で自分自身の設置ディレクトリを求める定番イディオムが realpath / readlink -f の典型用途。シンボリックリンク経由で起動されても実体ディレクトリを取得できる。

シェルスクリプトが「自分の置かれたディレクトリ」を基準に他ファイルを参照したい場面は多い。realpath を使うと symlink 経由で呼ばれても実体の場所を解決できる。

#!/bin/bash
# スクリプト自身の実体ディレクトリを取得
script_path=$(realpath "$0")
script_dir=$(dirname "$script_path")

echo "実行中のスクリプト: $script_path"
echo "設置ディレクトリ:   $script_dir"

# 同じディレクトリの設定ファイルを安全に参照
config="$script_dir/config.env"

realpath が無い最小環境向けには readlink -f "$0" で代替できる。両者はデフォルトで同じ「最後の要素以外は存在必須」の挙動になる。

macOS など BSD 系の readlink には -f が無い(または挙動が異なる)。GNU coreutils を前提にできない可搬スクリプトでは、realpath の有無を確認するか cd "$(dirname "$0")" && pwd 系のイディオムを検討する。

次に読む