【ちょっとしたこと】Linux のシンボリックリンクを作成する際にちょっと困ったこと

記事タイトルとURLをコピーする

こんにちは。カスタマーサクセス部の山本です。🐱

シンボリックリンクのファイル名を指定しない方法と、する方法

Linux サーバーにおいて、ln -s (コマンドとオプション)を使用して、シンボリックリンクを作成することがあると思います。
その際に、シンボリックリンクのファイル名を指定しない方法と、する方法があります。
後者の方法でシンボリックリンクを作成している場合、ちょっと困ったことがありましたので、ご紹介します。

ln -s では、
第一引数に参照先のディレクトリやファイルへのフルパス
第二引数に参照元のディレクトリやファイルへのフルパス
を取ります。

第二引数にディレクトリを指定しファイル名を指定しない場合と、ファイル名まで書く場合があります。
ちょっと困ったのはここです。以下に例を示します。

  1. ディレクトリを指定しファイル名を指定しない例です。ディレクトリ a の中に ディレクトリ b を参照するシンボリックリンク b を作成します。:
    • ln -s /home/ec2-user/b /home/ec2-user/a
  2. ファイル名を指定する例です。ディレクトリ a の中に ディレクトリ b を参照するシンボリックリンク b を作成します。:
    • ln -s /home/ec2-user/b /home/ec2-user/a/b

上記 2つのコマンドは、同じ結果になります。
1 では、リンクの参照先ディレクトリ b がシンボリックリンクのファイル名になるためです。 ディレクトリ /home/ec2-user/a の中に b というリンクファイルができます。

1 の結果です。

2 の結果です。

ファイル名を指定しない方法と、する方法の違い

ファイル名を指定しない 1 のコマンド(ln -s /home/ec2-user/b /home/ec2-user/a)を複数回実行すると、2 回目にエラー ("File exists") になり、リンクは 1 回目にのみ作成されます。
ファイル名を指定する 2 のコマンド(ln -s /home/ec2-user/b /home/ec2-user/a/b)を複数回実行すると、3回目にエラー ("File exists") になり、リンクは 1 回目と 2 回目に作成されます。

同じ結果になるコマンドであるものの、複数回実行時の結果が異なります。

ファイル名を指定しない 1 (ln -s /home/ec2-user/b /home/ec2-user/a )は私の思うあるべき姿・メンタルモデルに合っている形です。
ファイル名を指定する 2 (ln -s /home/ec2-user/b /home/ec2-user/a/b)は私には最初わかりませんでした。
2 の場合には、以下に説明する動作をします。

説明:

  1. 1 回目の実行では、ディレクトリ b を参照するシンボリックリンク b が、ディレクトリ a 内にある状態になる。
  2. 2 回目の実行では、1 回目でできたシンボリックリンク b の参照先であるディレクトリ b の中に、ディレクトリ b を参照するシンボリックリンク b ができる。
    • 2 回目にできるシンボリックリンクは循環参照するリンクとなる。

実際の様子を見てみましょう。

  • ファイル名を指定しない 1 のコマンド(ln -s /home/ec2-user/b /home/ec2-user/a
    • ▶ 2 回目の ln -s で「ファイルが既にある」エラーになります。

  • ファイル名を指定する 2 のコマンド(ln -s /home/ec2-user/b /home/ec2-user/a/b
    • ▶ 2 回目の ln -s でディレクトリ b の中にもシンボリックリンクができます。
    • ▶ 3 回目の ln -s で「ファイルが既にある」エラーになります。

ちなみに、ファイル名を指定する 2 のコマンドにおいて、ファイル名を b と異なる c にした結果です。b のときと同じです。

どういうときに困るの?

ファイル名を指定する 2 のコマンド(ln -s /home/ec2-user/b /home/ec2-user/a/b)では、2 回目の実行で「シンボリックリンクが既にある」というエラーになりません
そのため、シンボリックを作成する前処理として、「既にシンボリックリンクがあるかどうかを確認する処理」が必要になる場合があります。
例えば、サーバーへのデプロイ処理の中にシンボリックリンクを作成する処理があるとします。
誤って、 2 回デプロイしてしまった際に、エラーになる想定だったのにならない、といったことがあるかも知れません。

本来ならば参照先となるフォルダに、そのフォルダを循環参照するシンボリックリンクができてしまうのも、良くない点です。
同じシンボリックリンクが、シンボリックリンクの参照先にもあったりすると、サーバー上で作業している際に混乱してしまうと思います。
Linux のシンボリックリンクは CUI で作業するため、自分がどこにいて、どこを参照しているのか、分かりにくい状況があります。

気を付けること

可能であれば、ファイル名を指定しないで作成するのが良いと思います。
ほとんどのケースでは事足りるのではないかな、と思います。

テキストログ

  • ln -s /home/ec2-user/b /home/ec2-user/a
[ec2-user@ip-10-0-15-46 ~]$ mkdir {a,b}
[ec2-user@ip-10-0-15-46 ~]$ ll
total 0
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:15 a
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:15 b
[ec2-user@ip-10-0-15-46 ~]$ touch b/b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll b
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:16 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ pwd
/home/ec2-user
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a
[ec2-user@ip-10-0-15-46 ~]$ ll a
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:16 b -> /home/ec2-user/b
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ll a/b/
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:16 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a
ln: failed to create symbolic link '/home/ec2-user/a/b': File exists
[ec2-user@ip-10-0-15-46 ~]$
  • ln -s /home/ec2-user/b /home/ec2-user/a/b
[ec2-user@ip-10-0-15-46 ~]$ mkdir {a,b}
[ec2-user@ip-10-0-15-46 ~]$ ll
total 0
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:30 a
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:30 b
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ touch b/b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ pwd
/home/ec2-user
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/b
[ec2-user@ip-10-0-15-46 ~]$ ll a/b/
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:31 b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll a
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:31 b -> /home/ec2-user/b
[ec2-user@ip-10-0-15-46 ~]$ ll b
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:31 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/b
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ll a/b/
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:31 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:31 b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll a
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:31 b -> /home/ec2-user/b
[ec2-user@ip-10-0-15-46 ~]$ ll b
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:31 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:31 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/b
ln: failed to create symbolic link '/home/ec2-user/a/b/b': File exists
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$
  • ln -s /home/ec2-user/b /home/ec2-user/a/c
[ec2-user@ip-10-0-15-46 ~]$ mkdir {a,b}
[ec2-user@ip-10-0-15-46 ~]$ ll
total 0
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:19 a
drwxr-xr-x. 2 ec2-user ec2-user 6 Mar 25 09:19 b
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ touch b/b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll b
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ pwd
/home/ec2-user
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/c
[ec2-user@ip-10-0-15-46 ~]$ ll a
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:19 c -> /home/ec2-user/b
[ec2-user@ip-10-0-15-46 ~]$ ll a/c/
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/c
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ll a/c/
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:20 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ll a/c/b/
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:20 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll a/c/b/b/
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:20 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$ ll a/c/b/b/b/
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:20 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ll b
total 0
lrwxrwxrwx. 1 ec2-user ec2-user 16 Mar 25 09:20 b -> /home/ec2-user/b
-rw-r--r--. 1 ec2-user ec2-user  0 Mar 25 09:19 b.txt
[ec2-user@ip-10-0-15-46 ~]$
[ec2-user@ip-10-0-15-46 ~]$ ln -s /home/ec2-user/b /home/ec2-user/a/c
ln: failed to create symbolic link '/home/ec2-user/a/c/b': File exists
[ec2-user@ip-10-0-15-46 ~]$

動作確認した環境

Amazon Linux 2023 と、手元の Mac Book Pro の Bash で確認しました。
確認していないものの、Bash 環境であれば、同じだと思います。
uname -a の結果も載せておきます。

  • Amazon Linux 2023

    • Linux ip-10-0-15-46.ap-northeast-1.compute.internal 6.1.72-96.166.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jan 17 00:42:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
  • Mac Book Pro

    • Darwin hogehoge.local 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:33:31 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T8112 arm64

まとめ

Linux のシンボリックリンクを作成する際にちょっと困ったことについて、書きました。
誰かのお役に立てると幸いです。

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア。2024 Japan AWS Top Engineers に選んでもらいました。

今年の目標は Advanced Networking – Specialty と Machine Learning - Specialty を取得することです。

山を走るのが趣味です。今年の目標は 100 km と 100 mile を完走することです。 100 km は Gran Trail みなかみで完走しました。残すは OSJ koumi 100 で 100 mile 走ります。実際には 175 km らしいです。「草 100 km / mile」 もたまに企画します。基本的にのんびりした性格です。