POSIXで標準化されているパスが /, /dev, /dev/console, /dev/null, /dev/tty, /tmp の6つしかない理由

## はじめに

実は POSIX で標準化されているファイルまたはディレクトリのパスはたったの 6 つしかありません。その中で途中のパスであるため明らかに必要だとわかる `/` と `/dev` を除くとたったの 4 つです。POSIX に準拠したシステムであっても `/bin`, `/etc`, `/home`, `/usr`, `/var` のような Unix / Linux 系 OS なら有るのが当然に思えるディレクトリすら POSIX では標準化されていません。

[POSIX - Directory Structure and Files](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap10.html)

```
/
+-- dev/
|   |- console
|   |- null
|   `- tty
`- tmp/
```

実は UNIX を開発した AT&T は [SVID (System V Interface Definition)](http://www.sco.com/developers/devspecs/) という UNIX の仕様を POSIX 登場と同じ頃に策定しており、その Volume 1a (P64) では以下の見慣れたディレクトリやファイルが規定されています。（[SVID についての参考記事](https://qiita.com/ko1nksm/items/7d75a700ef1be2722372)）

```
/
+------------+--------------+--------------+
|- opt/      `- usr/        `- dev/        `- etc/
|- proc/        |- bin/        |- console     |- group
|- tmp/         |- sbin/       |- null        |- passwd
|- sbin/        |- share/      `─ tty         `- profile
`- var/         `- lib/
```

補足 `/dev/stdout` や `/dev/stderr` は POSIX でも SVID でも標準化されていません。また `/home` ディレクトリも標準化されていません。古い時代には `/usr/home` がその名の通りユーザーのホームとして利用されており、後にディスク不足で `/home` に分離され `/usr` はユーザーランド用のプログラムとデータという意味に変わったという経緯があるようです。当時はシステムで決める場所では無かったということなのでしょう。（参考「[At what point did the /home directory appear?](https://unix.stackexchange.com/questions/121258/at-what-point-did-the-home-directory-appear)」「[What is the history of when and why /home/ moved out of /usr/ in Linux?](https://www.reddit.com/r/linux/comments/giaz0j/what_is_the_history_of_when_and_why_home_moved)」）

SVID と POSIX でこのような違いがあるのは SVID が「Unix が備えるべき標準仕様」であるのに対して、POSIX は「高い移植性のための OS とアプリケーションのインターフェースの標準規格」であるからです。[^1]

余談ですが `/bin` って SVID で標準化されてないんですね。Solaris で `/bin` が `/usr/bin` へのシンボリックリンクになってる理由は、Unix にとっては `/bin` は必須なものではなく互換性のために作られたものと考えて良さそうです。

## 標準化されてない理由

### アプリケーションにとって必要ないものだから

POSIX は OS (Unix) を作るための規格ではなく、OS とアプリケーションのインターフェースを標準化したものです。そのためアプリケーションを動かすのに必要なもの以外は標準化されていません。

例えば `/etc` はシステムの設定ファイルを保存として一般的に使われていますが、そこに設定ファイルを保存しなければいけないわけではないので必須なものではありません。`/sbin` や `/usr/bin` にある標準コマンドも環境変数 `PATH` 経由で呼び出すことさえできれば良いわけで別のディレクトリにあっても構いません。`/proc` ファイルシステムも、それがあるとシェルスクリプト等からファイルとして情報を参照できて便利というだけで、システムコールを呼び出せばいいので不要です。実際に macOS には `/proc` ファイルシステムはありません。

そう考えると Unix 系 OS が標準的に持っている多くのディレクトリやファイルは、OS を形作るための内部構造としては必要であっても、アプリケーションを動かすのに必須なものではないということに気づきます。

### 高い移植性を実現するため

標準化されてないものは互換性が低いのに、なんで標準化されてない方が移植性が高くなるんだ？と思うかもしれませんが、POSIX は「高い移植性を実現したいなら標準化されてないものは使うな」という方針なので標準化されていないものに互換性がなくても関係ありません。

SVID は多くのものを標準化しそれに従わせることで「互換性がある Unix」だけの世界を作り移植性を高めようとしましたが、POSIX では逆にアプリケーションを動かすのに必要最低限のインターフェースだけ標準化することで「より多くの OS に対して移植性を持たせる事」を実現しました。

例えば POSIX ではユーザー管理やシステム管理系のコマンドは標準化の内容から意図的に省かれています。POSIX にとってそれらはアプリケーションを動かすために必要なものではなく OS の内部構造（のためのコマンド）にあたります。この方針により「POSIX 準拠の OS とのインターフェース」さえ備えていれば、Windows NT [^2] のように Unix とは全く異なる方法で管理する OS であっても移植性を実現することが可能となりました。もし Unix 流儀のコマンドやディレクトリ構造を POSIX で要求してしまったら、OS ベンダーの開発の負担が大きくなり、Windows を含むあらゆる OS との間で移植性を実現することは不可能だったでしょう。

POSIX はありとあらゆる OS での移植性を実現するのが目的であり、そのためにアプリケーションが依存してよい（= OS が実装しなければいけない）ディレクトリやファイルを必要最小限にまで減らしたわけです。

### 拡張性を実現するため

一度ディレクトリとその役目を決めてしまうと将来の拡張性に制限ができてしまいます。例えばコマンドは `/usr/bin` 以下に入れるものと決めてしまえば、将来の改良でそれ以外の場所に変更するのが難しくなりますし、全く別のディレクトリ構造（Windows や macOS を想像してください）に変更することもできなくなってしまいます。POSIX の標準規格の中に含まれていなければ、どんな大胆な変更であっても POSIX に準拠したまま行うことが可能となります。

POSIX ではベンダー独自の仕様を「POSIX に準拠してない勝手な拡張機能」ではなく「POSIX に準拠しつつ行った素晴らしい拡張機能」として考えており、POSIX はベンダー独自の拡張機能の実装が妨げられることがないように十分考慮して拡張可能なように標準規格の内容を策定しています。その一つとして「標準化の内容に含めない」ことが極めて重要なことなのです。標準規格の内容と矛盾しないなら何を実装しても構いません。ベンダーの独自仕様を禁止しなかったことが POSIX が普及した理由の一つです。

## 標準化されてるディレクトリとファイルの理由

POSIX で標準化されているディレクトとファイルは、`/` と `/dev` を除くと `/dev/console`、`/dev/null`、`/dev/tty`、`/tmp` の 4 つです。ここまでで解説した通り POSIX はこれだけしか標準化しなかったのではなく、標準規格に含めるものを可能な限りなくした結果、残ったのがこの 4 つなのです。これらはアプリケーションにとってどうしても必要なものという事です。さて、ではなぜこれらのパスは POSIX の標準規格の内容から外す事ができなかったのでしょうか？

### `/dev/console`

アプリケーションで `/dev/console` 使ってる例はあまり見かけず、これらは POSIX で標準化する必要があるのだろうか？と悩むところですが、実は `/dev/console` は元々 POSIX で標準化されたものではありません。POSIX.1-2001 で SUS (Single UNIX Specification) の仕様が統合された時に、SUS の仕様がマージされたものです。POSIX としては本来必要とは考えていなかったもので、SUS との互換性が理由です。

[Rationale - Directory Structure and Devices](https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap10.html)

> The standard file /dev/console has been added for alignment with the Single UNIX Specification.

おそらく [SUS で必須](https://www.opengroup.org/openbrand/register/xv7m0.htm)となってる POSIX への追加機能の [X/Open Curses](https://publications.opengroup.org/c094) を実装するために必要なんじゃないかと思っていますが詳細は未確認です。

### `/dev/null`

出力を捨てる時や空ファイルからの入力として、アプリケーションから広く利用されています。

### `/dev/tty`

入出力がリダイレクトされていても、必ず端末から入出力したい場合に使うことができます。これは例えばデバッグのために必ず画面に出力するときや、リダイレクトを許さない端末からのパスワード入力に利用することができます。また指定されたファイルを読み書きするようなアプリケーションで `/dev/tty` をファイル名として指定することでファイルの代わりに端末を利用することが可能になります。

補足ですが `docker run` で `-t` (Allocate a pseudo-TTY) オプションを指定しない場合、コンテナ内で `/dev/tty` は存在しません。CI 環境でも `/dev/tty` が存在しないことがあります。一部の環境で存在しない場合があるので少し注意が必要です。

### `/tmp`

一部の（歴史的な）アプリケーションが `/tmp` ディレクトリを一時的なデータを保存する場所として利用するために残されています。推奨は環境変数 `TMPDIR` で設定されているディレクトリを一時的なデータを保存する場所として利用することですが `TMPDIR` が空の場合などに `/tmp` が代わりに使われることが多いです。

## 標準化されてるパス以外は使ってはいけない？

ここでいう「使ってはならない」の意味は、アプリケーションのソースコードの中で決め打ちで使うかどうかの話です。コマンドの引数や設定ファイルなどユーザーが与えるパスの話ではありません。

たしかにアプリケーションのソースコードの中で POSIX で標準化されているパス以外を使ってはいけません。ただしそれは「POSIX 定義による移植性を実現する」という前提の話です。移植性を犠牲にするならば使って構いません。移植性を犠牲にすると言っても移植性が 0 になるわけではありません。100 から 99 になる程度です。

例えば POSIX ではシバン（シェルスクリプトなどの一行目に書く `#!/bin/sh` など）は標準化されていません。なぜなら `/bin` というパスが標準化されていないからです（実際に Android では `/bin` は存在せず代わりに `/system/bin` があります）。そのため POSIX では「[シバンを指定した場合の動作は未定義（unspecified）](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html#tag_18_01)」となっており「[POSIX に厳密に準拠したいアプリケーションではシバンを使用しないでください]
(https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/xrat/V4_xcu_chap02.html#tag_23_02_01)」と書かれています。しかしこれはあくまで POSIX に準拠した世界だけの話です。現実には POSIX シェル以外のシェルや POSIX に完全に準拠してない環境が使われているためそれら含めるとシバンを書いた方が移植性は高くなります。私の「移植性が高い」の定義は「現実で使われているより多くの環境で動く方が移植性が高い」です。この話については [シバン (shebang) を書いた方が可搬性が高いシェルスクリプトになる](https://qiita.com/ko1nksm/items/c244752c7f3d0df5e723) で詳しく解説しています。

少し話がそれますが POSIX の面白い所は、ファイルの頭の `#!` の定義で POSIX では「拡張機能のために予約されている」と定義されている所です。拡張機能というのはシバンの仕組みのことです。つまり POSIX は「ファイルの頭の `#!` を拡張機能（シバン）として実装しても良いですよ」と言っているのです。もしこのこと明確に規定していない場合 `#` で始まる行はすべてコメントとみなすのがシェルスクリプト（シェルコマンド言語）の定義であるためシバンを実装できなくなってしまいます。シバンの定義はできませんが、シバン"**等**"の拡張機能の仕組みに利用して良いと POSIX は定義しており、シバンに限定してないのは、シバン以外の拡張機能を実装できる余地を残すためです（例えば `#!/bin/sh` を `#!/usr/bin/sh` に自動的に読み替える拡張機能を実装しても POSIX 違反にならない）。

話を戻しますが、POSIX に厳密に準拠したアプリケーションでは POSIX で標準化されてないパスをアプリケーションで使っていけないという話はシバンだけに限りません。例えば環境変数 `PATH` の初期化にも当てはまります。例えばシェルスクリプト（アプリケーション）の冒頭で `PATH="/bin:/usr/bin"` と初期化をしている場合、この `/bin/` や `/usr/bin` は POSIX で標準化されてないパスであるため、POSIX の定義では移植性が低くなることを意味します。実際に Solaris 11 ではこのディレクトリに POSIX に準拠してない歴史的なコマンドが置いてあるため問題になります。POSIX の考え方ではシェルスクリプトの中で環境変数 `PATH` を設定せずに外部から与える（もしくは `getconf PATH` で取得する）が正解です。一般的に環境変数は"環境"の設定なのでシェルスクリプト（アプリケーション）の外で行うのが原則です。

POSIX で標準化されていないパスをアプリケーションで決め打ちで使わない方が移植性は高くなるはそのとおりです。しかしそれは現実的でない場合があり、POSIX に従わない方が現実的にはより多くの環境に対応することができることも多いです。また移植性が高い方が良いと言っても、すべての環境に対応する必要なんて現実的にはほとんどありません。そのため標準化されているパスだけを使うべきかどうかというのは、結局の所アプリケーションが対応する環境を考慮してケースバイケースで決める話です。例えば特定の環境でしか使わないスクリプトであれば、その環境限定のディレクトリやファイル使った所で誰も困りません。

## 参考 ディレクトリ構造のためのその他の標準規格

### 各 OS ベンダーのための標準規格

POSIX が（Unix 系 OS に限らない）OS とアプリケーションのインターフェースだけを定義した標準規格であるため、必要最小限のディレクトリファイル以外は POSIX では標準化されていません。それだけでは OS としては成り立ちませんから OS としてのディレクトリ構造はそれぞれの OS ベンダーが作る必要があります。それらがバラバラだと Unix ユーザーは混乱しシステム管理はしづらくなるのでこちらにも標準規格があります。

それらについてはこの記事の範疇外ですので詳しくは解説しませんが、その中で最も有名なのが [FHS (Filesystem Hierarchy Standard)](https://refspecs.linuxfoundation.org/fhs.shtml) でしょう。これは主に Linux のためのディレクトリ構造を標準化したものです。FHS 公式では 1995年に [Linux 以外の Unix 系の標準となることも目標の一つになった](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch07s02.html)と主張していますが、現実にはそうはならなかったようです<sup>[[1](https://lists.freebsd.org/pipermail/freebsd-fs/2011-May/011439.html)] [[2](https://mail-index.netbsd.org/tech-userlevel/2011/05/10/msg005050.html)] [[3](https://www.mail-archive.com/tech@openbsd.org/msg07135.html)]</sup>。また FHS の母体である [LSB](https://ja.wikipedia.org/wiki/Linux_Standard_Base) が（Debian の離脱で影響力を失ったため？） 2015 年で活動を停止しており FHS も停止状態です。とはいえ FHS は単に既存の慣習を明文化しているだけで、POSIX と同じく自らが先導になって仕様を作っているわけではないので無くなった所で大きな影響はありません。

BSD 系はそれぞれ独自のディレクトリ構造を持っており、それらは [hier](https://www.freebsd.org/cgi/man.cgi?hier(7)) というドキュメントに明記されています。macOS も BSD 系が含まれているため `man hier` でドキュメントが読めますが、これらは歴史的なディレクトリ構造ということで、モダンなディレクトリ構造は [macOS や iOS の File System Programming Guide](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) で解説されています。その他 Unix もそれぞれのディレクトリ構造を持っています（[Solaris](https://docs.oracle.com/cd/E36784_01/html/E36883/filesystem-5.html)、[AIX](https://www.ibm.com/docs/ja/aix/7.2?topic=tree-file-system-structure)）。とはいえ、いずれも歴史的な理由で UNIX のディレクトリ構造を踏まえているので、だいたい似たような構造となっています。

### XDG Base Directory Specification

OS ベンダーのための標準規格ではありませんが [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) と呼ばれる比較的新しく作られた（2003 年頃？）標準規格があります。これは主にホームディレクトリのディレクトリ構造（ドットファイル等）とそれを見つける環境変数を定義しています。ホームディレクトリは混沌となりがちでありながら標準となる慣習も存在しなかったこともあり、[多くのアプリケーション](https://wiki.archlinux.org/title/XDG_Base_Directory)が XDG Base Directory Specification に対応しています。これはアプリケーション開発のための仕様でありながら POSIX には欠けていたものであるため、アプリケーション開発者にとって重要な標準規格となっています。

## まとめ

ということで、POSIX でわずか 6 つのディレクトリとファイルしか標準化してない理由は、POSIX が OS とアプリケーションのインターフェースの標準規格であり、それら以外はアプリケーションにとって必要ないと判断しているから。またそれらを POSIX から除外しているからこそ高い移植性と拡張性を実現できているという話でした。

[^1]: POSIX が Unix を作るための規格でないというのは、分かりやすく言えば、何も知らない状態で POSIX に準拠するだけの OS を作ったとしても Unix や Linux に近い OS が出来上がることはないということです。

[^2]: 昔の Windows NT は POSIX に準拠しています。