はじめに
しばらく真面目な記事を書き続けていたので、閑話休題ということでこの記事では自分の仕事での失敗談を話していく。
筆者は新卒でインフラのバックグランド0の状態でやる気だけ持ってインフラ関係の部署に配属された人間である。この話はよくコマンドの内部構造を理解せず強い力を行使してしまったというものである。
事故の概要
下記のようなディレクトリ構成があってmodule1→module2にシンボリックリンクが貼られていた。(本来は同じディレクトリ名ではあるが説明用に名前を分けている)
infra-files/
├ project/
└ module2
└ module1
このような構成でmodule2のシンボリックリンクを削除したいとなったときに自分が打ったコマンドは、
rm -rf module2
確かにmodule2は抹消された。だがmodule1もこの時抹消されてしまったのだ。
rmとunlinkの違い
シンボリックリンクを削除するときよく言われるのはunlinkを使えというものだろう。
ではunlinkとrmの違いは何か、それは内部処理として再帰的にファイルを探索するかである。unlinkはシステムコールであり、rmはunlinkとrmdirを組み合わせていい感じにリッチにしたコマンドであったのだ。
それぞれのmamページを見ていこう。
-
rm
- ファイルやディレクトリの削除を行う
- https://manpages.ubuntu.com/manpages/jammy/ja/man1/rm.1.html
-
unlink
- unlink 関数を呼び出し指定されたファイルを削除する
- https://manpages.ubuntu.com/manpages/jammy/ja/man1/unlink.1.html
このようにunlinkもrmの機能もファイルを削除するコマンドであることがわかる。
実際のコードの中身を見てみよう
https://github.com/coreutils/coreutils/blob/master/src/remove.c#L394
unlinkatはunlinkの上位互換コマンドであるため同様の中身であることがわかる。
コード内の以下の部分を見ると、rmはディレクトリツリーを構成して、そこを走査してファイルを削除していっていそうなことがわかる。
enum RM_status
rm (char *const *file, struct rm_options const *x)
{
// ...
FTS *fts = xfts_open (file, bit_flags, nullptr);
while (true)
{
FTSENT *ent = fts_read (fts);
// ...
enum RM_status s = rm_fts (fts, ent, x);
// ...
}
}
まとめ
rmコマンドはなんでも消せる、実際消せた。
自分はそういう理解で生きてきたが、小さいものに大きすぎる力で挑むと誤って周りに被害を出してしまうかもしれない。
自分と同じことを起こす人が1人でも減るように祈っている。