# 登录服务器后，先看懂路径和顶层目录

绝对路径、相对路径、~ 与 . .. 的规则，以及 /etc、/var、/home、/usr 各自放什么、该去哪找东西

> 文件系统 · 路径 · Linux 基础 · 约 7 分钟 · 09 月 23 日

## 本篇要点

1. 以 / 开头的路径是绝对路径，从文件系统顶端开始数；不以 / 开头的是相对路径，从当前工作目录开始数，和命令或脚本文件所在的位置无关。
2. . 表示当前目录，.. 表示上一层目录，在 / 里执行 cd .. 仍停在 /；~ 不是真实目录名，而是 shell 用你的 home 目录（通常取自 $HOME）做的替换。
3. 顶层目录沿着“会不会一直变”和“属于本机还是属于发行版”两条线划分：/usr 是发行版装进来的软件本体，基本只读；/etc 是本机配置；/var 是持续变化的数据，日志在 /var/log、状态数据在 /var/lib；/home 是普通用户家目录，root 的家目录在 /root。
4. /tmp 重启清空，/var/tmp 默认跨重启保留，/run 是开机新建、关机清空的运行期数据，三者都不适合放需要长期保留的文件。
5. /proc 是内核生成的虚拟文件系统，读 /proc/<pid> 下的文件就是在查询进程状态。
6. 现代发行版上 /bin、/sbin、/lib 通常是指向 /usr/bin、/usr/sbin、/usr/lib 的符号链接，用 ls -ld /bin 看首列是否为 l 即可确认；老路径仍然可用，不必纠结命令该写哪个路径。
7. 服务日志可能被 systemd 的 journald 收进二进制日志而不是写在 /var/log，找不到时改用 journalctl -u 服务名。

---

SSH 连上去，屏幕上是一个闪动的提示符。接下来无论你要干什么——找配置文件、看日志、传代码——第一件事都是把一个位置说清楚。Linux 里说位置的方式就是路径，而路径写在哪棵树上的，就是文件系统。

这篇只解决两个问题：路径怎么写才不会有歧义，以及 `/etc`、`/var`、`/home`、`/usr` 这些顶层目录各自负责什么、你要找东西时该敲哪扇门。

## 路径的两种写法，区别在“从哪儿开始数”

先敲一个命令：

```
pwd
```

它打印出来的东西，比如 `/home/deploy`，就是你此刻站的位置，叫**当前工作目录**。所有不以 `/` 开头的路径，都会被解释成“从当前工作目录往下走”。

于是分出两类路径：

- **绝对路径**：以 `/` 开头，从文件系统最顶端开始数，比如 `/etc/nginx/nginx.conf`。不管你此刻站在哪，它指向的都是同一个文件。
- **相对路径**：不以 `/` 开头，从当前工作目录开始数，比如 `logs/app.log`。

举个具体的：假设你在 `/home/deploy`，依次敲

```
cd /var
cd log
```

两行之后你到了 `/var/log`。第二行的 `log` 是相对路径，被拼在 `/var` 后面，和直接敲 `cd /var/log` 结果一样。反过来，如果你在 `/home/deploy` 直接敲 `cd log`，那它找的是 `/home/deploy/log`，多半会报 `No such file or directory`——不是你敲错了字，是从错误的地方开始数的。

这里有个容易踩的坑：**相对路径永远是相对“你敲命令时所在的目录”，而不是相对“命令文件或脚本所在的位置”**。同一个脚本，从不同目录执行，里面的相对路径指向的东西可能不一样。这也是服务器上的自动化脚本几乎都写绝对路径的原因——脚本得能在任何地方被调用。

还有一点：Linux 路径区分大小写，`Logs` 和 `logs` 是两个不同的东西。

## `.`、`..` 和 `~`：三种缩写

目录里一直藏着两个条目，用 `ls -a` 才看得见：

- `.` 表示当前目录自己。所以 `./run.sh` 的意思是“运行当前目录下的 run.sh”，而不是让 shell 去别的地方找。
- `..` 表示上一层目录。在 `/var/log` 里敲 `cd ..` 就到了 `/var`；在系统顶端的 `/` 里敲 `cd ..`，还停在 `/`——根目录没有上一层，它自己就是自己的上一层。

所以 `cd ../../etc` 是“往上两层，再进 etc”。这种写法临时跳转时省事，但它依赖你此刻站在哪，写进笔记和脚本就不合适了。

`~` 是另一回事，它**不是文件系统里的真实目录名，而是 shell 帮你做的替换**：你敲 `cd ~`，shell 先把它换成你的 home 目录（通常取自环境变量 `$HOME`），再把结果交给系统。所以：

- `~` 就是你自己的 home 目录，比如 `/home/deploy`；
- `~/app/config.yml` 等于 `/home/deploy/app/config.yml`；
- 用 `ssh deploy@server` 登录时，`~` 指的是远端那个 deploy 用户的 home，和你本机无关；
- root 用户的 `~` 是 `/root`，不是 `/home/root`，原因下面会讲到。

## 顶层目录：看它归谁管、会不会一直变

`ls /` 会列出根目录下十几个名字。它们不是随手起的，抓住两个问题就够用：这个目录里的东西，是装好系统后基本不动，还是系统一跑起来就一直在写；以及它是这台服务器自己特有的，还是所有装同款系统的机器都一样。

- **`/usr`：发行版装进来的软件本体。** 命令在 `/usr/bin`，共享库在 `/usr/lib`，与硬件架构无关的数据（文档、字体、时区表）在 `/usr/share`。它主要由包管理器写入，正常运行时应该是只读的，你不该手动往里塞文件。`/usr/local` 是例外，留给管理员自己编译安装的软件，目录结构和 `/usr` 对称。
- **`/etc`：这台机器自己的配置。** `/etc/nginx/nginx.conf`、`/etc/ssh/sshd_config`、`/etc/hosts`、`/etc/fstab` 都在这里。凡是“同一份软件在这台机器上和在那台机器上表现不同”的原因，几乎都能在 `/etc` 里找到。它里面放的是文本配置，不是可执行程序。
- **`/var`：会持续变化的数据。** 这是 `/usr` 和 `/etc` 的对立面：日志在 `/var/log`，缓存和临时产物在 `/var/cache`，服务自己的状态数据（数据库文件、索引、持久化的队列）在 `/var/lib`，等待处理的队列在 `/var/spool`。你排查问题的第一现场通常在这个目录里。
- **`/home`：普通用户的家目录。** `/home/deploy` 就属于 deploy 这个账号。它可能挂在网络存储上，也可能要到启动后期才可用，所以系统服务的配置里很少直接写它。而 root 的家目录故意放在 `/root`，跳出 `/home`，这样即使 `/home` 没挂上，root 也能登录进去修。
- **`/tmp`、`/var/tmp`、`/run`：临时数据的三兄弟，寿命不同。** `/tmp` 放小临时文件，通常直接跑在内存里，重启就清空；`/var/tmp` 放更大、希望跨重启保留的临时文件，默认不随重启清掉；`/run` 是系统启动后新建、关机即清的一层，放进程的 socket、pid 和运行期状态，它在启动最早阶段就可写。这三个地方都别放你真在乎的东西。
- **`/proc` 不是真正的磁盘目录**，而是内核实时生成的虚拟文件系统，读它就是在向内核提问。比如 `/proc/<pid>/cmdline` 能告诉你某个进程是怎么启动的，`/proc/<pid>/exe` 指向它的可执行文件。后面讲进程时会经常回到这里。

`ls /` 里剩下的名字大多不用急着关心：`/boot` 放内核和引导程序，除了升级内核基本不碰；`/opt` 留给第三方附加软件包；`/dev` 下是一个个设备文件。

关于 `/var/log` 有一点要提前知道：传统上服务的日志是文本文件写在 `/var/log` 下的，但现在很多服务把日志交给 systemd 的 journald 统一收进二进制日志，要用 `journalctl -u 服务名` 查看，`/var/log` 里可能根本找不到对应文件。找不到日志时先想想是不是这种情况。

## 一个真实的困惑：`/bin` 和 `/usr/bin` 到底是不是一个地方

第一次认真看根目录的人常会卡在这里：`/bin`、`/sbin`、`/lib` 看起来和 `/usr/bin`、`/usr/sbin`、`/usr/lib` 平级，名字也像，里面还装着很多同名命令。到底哪个是真的？

答案是：**在现代发行版上它们通常是同一个地方，前者只是指向后者的符号链接。** 符号链接就是一个指向另一个路径的条目。用这条命令可以自己验证：

```
ls -ld /bin /sbin /lib
```

如果输出第一列是 `l`（表示 link），并显示 `/bin -> usr/bin` 这样的箭头，那它就是个符号链接——访问 `/bin/sh` 和 `/usr/bin/sh` 得到的是同一个文件。

为什么会变成这样？历史上这个拆分有实在的理由：系统盘又小又贵，启动时 `/usr` 可能是一个单独的、还没挂载的分区，所以必须有一小批“至少能让我把 `/usr` 挂上”的命令住在根分区，这就是 `/bin` 和 `/sbin`。后来这个理由消失了：现代系统靠 initramfs 在启动早期就把 `/usr` 挂好，不再需要根分区自带一套救援工具。于是各发行版陆续做了“usr merge”——把文件本体统一收进 `/usr`，在根目录留下符号链接保证老路径仍然可用。Fedora 17（2012 年）就这样做了，Debian 从 bookworm 起只支持这种合并后的布局。systemd 从 256 版本起，如果检测到 `/usr/bin` 和 `/usr/sbin` 仍是两个分开的目录，启动时会打一行 `System is tainted: unmerged-bin`；按 Debian 发行说明的说法，这行提示可以忽略，但不要手工去合并。

理解这一点的实际价值是：你不用再纠结某个命令“到底属于 `/bin/xxx` 还是 `/usr/bin/xxx`”。想确认一个命令实际在哪里，用 `command -v 命令名` 看 shell 会调用哪个；想看清它最终解析到哪个文件，用 `readlink -f /bin/xxx`。

## 收成一句

找东西时先问自己要找的是什么性质的东西：**改行为去 `/etc`；找日志和运行数据去 `/var`；命令和软件本体在 `/usr`；属于某个用户的在它的 home；进程和内核状态在 `/proc`。** 写路径时，脚本和笔记里用绝对路径，交互操作时再用相对路径和 `~` 省事；不确定自己在哪，就先 `pwd`。

## 术语表

- 当前工作目录：shell 此刻所在的位置，所有相对路径都从这里开始解释，用 pwd 查看。
- 绝对路径与相对路径：绝对路径以 / 开头、结果唯一；相对路径不带 /、依赖当前工作目录。
- 符号链接：一个指向另一个路径的条目，ls -l 输出首列为 l 并显示箭头，readlink -f 可解析到最终位置。
- /etc：这台机器特有的配置目录，同一软件在不同机器上行为不同，原因多在这里。
- /usr：发行版通过包管理器安装的软件本体，运行时通常只读，命令在 /usr/bin、库在 /usr/lib。
- /var：随系统运行不断变化的数据，日志在 /var/log，服务状态数据在 /var/lib。
- /home 与 /root：普通用户家目录与 root 的家目录，后者故意放在 /home 之外，避免 /home 不可用时无法登录。
- /run：内存文件系统，启动后新建、关机清空，用来放 socket、pid 等运行期数据。
- journald：systemd 统一收集日志的组件，日志为二进制格式，用 journalctl 查看，不一定落在 /var/log。
- usr merge：把 /bin、/sbin、/lib 的内容并入 /usr 对应目录、原路径改为符号链接的布局调整。

## 来源

1. [Filesystem Hierarchy Standard 3.0 — 各顶层目录的定义，以及静态/可变、可共享/不可共享的划分](https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html)
2. [systemd file-hierarchy — 现代文件系统层级说明，/run、/tmp、/var/tmp、/var/log 与 journald 的关系](https://www.freedesktop.org/software/systemd/man/latest/file-hierarchy.html)
3. [The Case for the /usr Merge — /bin、/sbin、/lib 并入 /usr 并改为符号链接的原因与历史背景](https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/)
4. [Fedora Features/UsrMove — Fedora 17（2012）实施 usr move 的具体做法与新旧目录对照](https://fedoraproject.org/wiki/Features/UsrMove)
5. [Debian trixie 发行说明 — 关于 System is tainted: unmerged-bin 提示的说明](https://www.debian.org/releases/trixie/release-notes/issues.html)
6. [Debian 技术委员会决议 — bookworm 起只支持 merged-usr 布局的说明](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=978636)

---

原文：https://pangzhengboyin.com/articles/linux-paths-and-top-level-directories-8785e2c9

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
