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

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

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

先敲一个命令:

1pwd

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

于是分出两类路径:

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

举个具体的:假设你在 /home/deploy,依次敲

1cd /var
2cd log

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

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

还有一点:Linux 路径区分大小写,Logslogs 是两个不同的东西。

...~:三种缩写

目录里一直藏着两个条目,用 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 平级,名字也像,里面还装着很多同名命令。到底哪个是真的?

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

1ls -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