「シンボリックリンクの削除」に unlinkコマンドを使う理由
目次
こんにちは。システムソリューション部所属 なかです。
最近、SSL証明書の更新のためシンボリックリンクに触れる機会がありました。
その際に、改めてシンボリックリンクについて調べていると「シンボリックリンクの削除にunlink コマンドを使う」という情報が多く見受けられます。
ただし、以下のような点が気になりました。
「 シンボリックリンクの削除には”比較的”安全なだけで、間違えると普通に危ないのを説明してない事がある」
「コマンドやオプションの使い方が人によって微妙に違う」
「 たまにシンボリックリンクの削除用のコマンドと誤解している人もいる」
というわけで当記事では、
「シンボリックリンクの削除」にunlinkコマンドを使う理由や使い方がわかるよう、どのようなコマンドなのかも含めてご説明したいと思います。
実行環境
・OS:Ubuntu 20.04.5 LTS (WSL2環境)
・Shell:bash
・ロケールを日本語に変更
注意事項
当記事はLinuxにおける主要ディストリビューションであるRHEL系・Debian系における内容となっております。
SolarisなどのUNIX系ではunlinkコマンドの仕様に違いがあるためです。
unlink コマンドはファイルを削除するためのコマンド
un "link" と書かれているので シンボリック"リンク" を消すコマンドと誤解している方もいるかもしれませんが、
「シンボリックリンクも消せるだけで、本来はファイルを削除するためのコマンド」となっています。
Ubuntuでmanを叩いて確認してみると、下記のような説明がなされています。
$man unklink UNLINK(1) ユーザーコマンド UNLINK(1) 名前 unlink - unlink 関数を呼び出し指定されたファイルを削除する 書式 unlink FILE unlink OPTION 説明 指定した FILE を削除するために unlink 関数を呼び出します。 --help この使い方を表示して終了する --version バージョン情報を表示して終了する ※以下省略
説明の通り「指定されたファイルを削除する」コマンドと書かれています。
シンボリックリンクを消すためのコマンドではないため、普通に実体のファイルも消せるということです。
ファイルを削除する際の内部的な動作は rm コマンドと同じ
説明 指定した FILE を削除するために unlink 関数を呼び出します。
上記(man)で説明される「unlink 関数」とは「unlink」という、内部の根幹であるLinuxカーネルの機能を実行させる「システムコール」の事を指しています。
カーネルはこのシステムコールを受けて、指定されたファイルのハードリンクを削除することで、実態としてデータが消えます。
詳細についてはLinuxのファイル構造とハードリンク・シンボリックリンクの根本的かつ長い説明になるので割愛しますが、
要するに内部的にデータを削除する機能を呼び出すもので、このシステムコールは rm コマンドも使用しています。
ディレクトリを削除する「rmdir」システムコールと、ファイルを削除する「unlink」システムコールを使い
そこに色々なオプション機能を盛り込んだのが rmコマンドです。
つまり、ファイルを削除する事においては rmコマンドとunlinkコマンドはLinuxカーネルの内部的には同じ事をしています。
ファイルを消すコマンドという認識(危機感)を強くもっておく事が、安全に繋がって良いと筆者は考えます。
検証として削除用の実体ファイルを作って動作を確認する
検証のため「Testdate」というファイルを検証環境に用意します。
$ touch Testdate $ ls -l 合計 0 -rw-r--r-- 1 ubuntu ubuntu 0 9月 16 17:10 Testdate
ここで、unlink を使って実体ファイルを指定すると削除できる事が確認できます。
$ unlink Tstdate $ ls -l 合計 0
このように実体ファイルを削除するコマンドなので、シンボリックリンクを消そうとして元のリンク元ファイルを指定してしまうと当然消えます。
また、rm コマンドと違って -i オプションのような確認を出す誤操作予防機能は持っていないため要注意です。
rm コマンドとどう違うの?
unlink コマンドはディレクトリを消せず、ディレクトリ内のファイルを再帰的には消せない
これがシンボリックリンクを消す際に、このコマンドを使うことが一般的に推奨される理由です。
rmコマンドで誤って「リンク元ディレクトリ」や「シンボリックリンク先の配下を指定」してしまった際の削除事故を防げるからです。
どういうことなのかを、失敗実例で説明するため
「TestDir」というディレクトリとそれに対するシンボリックリンク「TestDir-link」を用意しました。
「TestDir」の配下には、検証用のテキストデータを3ファイル配置しています。
$ ls -l 合計 4 drwxr-xr-x 2 ubuntu ubuntu 4096 9月 16 18:01 TestDir lrwxrwxrwx 1 ubuntu ubuntu 7 9月 16 17:53 TestDir-link -> TestDir $ tree . ├── TestDir │ └── {TestDate1.txt}{TestDate2.txt}{TestDate3.txt} └── TestDir-link -> TestDir
「やってしまいがちな危険なrmコマンド失敗事例」紹介
BAD:リンク元ディレクトリを指定してしまった
$ rm -rf TsetDir
リンク元ディレクトリとシンボリックリンクの名前が似ている場合に起きやすい「リンク元ディレクトリ」を指定したケースです。
当然のことながらディレクトリごと全て消えます。
絶望が訪れます。
BAD:削除したい、シンボリックリンク「TsetDir-link」の後に「/」をつけてパス指定してしまった
$ rm -rf TsetDir-link/
これが一番やってしまいがちな間違えやすい記述で、ディレクトリを指定する際つい「/」をつけてしまうとシンボリックリンクではなく
「シンボリックリンク先の配下」を指定したことになります。
この場合は、ディレクトリの中を再帰的に対象にし、
{TestDate1.txt}{TestDate2.txt}{TestDate3.txt} の全データは儚く消えます。
「あれシンボリックリンク消えないな~?」と、ディレクトリの中身を確認した段階で絶望が訪れます。
※そもそも rmコマンドで -f オプションは軽々しく使ってはいけない凶悪な危険物ですが、
慣れている環境だと"つい"無意識に使ってしまうケースもあるようなので気をつけたいところです。
unlink コマンドなら不発に終わり、事故が起きにくい
上記の危険なケースにおいて、unlinkコマンドならその仕様から不発に終わります。
試しに実行してみましょう。
$ unlink TsetDir unlink: 'TsetDir' を削除 (unlink) できません: そのようなファイルやディレクトリはありません
これはディレクトリを削除できない仕様のため、リンク元ディレクトリを直接消すことはありません。
$ unlink TsetDir-link/ unlink: 'TsetDir-link/' を削除 (unlink) できません: そのようなファイルやディレクトリはありません
基本として直接的にファイル名を指定しないと消せない仕様のため、
ディレクトリ内を再帰的に処理できないので、ディレクトリの中身のファイル群を消すことはありません。
このように unlinkコマンドだと不発に終わる仕様のため、rmを使うことに比べてかなり事故リスクが減るわけですね。
まとめ:unlink コマンドがシンボリックリンクの削除に推奨される理由
rmコマンドでも、-i オプションをつけたり気をつければシンボリックリンクの削除に使用しても目的は果たせます。
ただし、人は失敗をする生き物で「それを無くすことは不可能です」
ならば事故を防止するため「そもそも人がミスできない、ミスしてもミスにならないようにする仕組み」が必要です。
この概念はフールプルーフと呼ばれる物で、これに基づいた運用として
作業をする際には、ミスが起きた時に影響が大きい rm より機能が限定されて影響が出にくいコマンドである unlink を使いましょうと言われる訳ですね。
という事で、本記事の解説は以上となります。
「unlink がなぜ推奨されるのか」を更に深掘りすると、「Linuxにおけるデータの構造」と「フールプルーフの大切さ」に触れることできます。
この記事をきっかけに、シンボリックリンク削除事故が減って貰えればになによりですし、Linuxの構造や安全性についても興味を持っていただければともおもいます。
ここまで読んでいただきまして、ありがとうございました!
参考資料
unlink(1) - Arch manual pages
https://man.archlinux.org/man/unlink.1.en