
如果你写过跨平台代码,大概率都被这两个符号折磨过:
java
// 看起来很自然,但跨平台时很容易埋雷
String path = "logs" + "/" + "app.log";
// 稍微稳一点
String path = "logs" + File.separator + "app.log";
在 Linux、macOS、Web URL 里,我们习惯看到:
text
/usr/local/bin
/Users/rock/Downloads
https://example.com/articles/hello-world
到了 Windows,画风突然变成:
text
C:\Users\rock\Downloads
D:\WorkSpace\Android\Milkitchen
更麻烦的是,反斜杠 \ 在很多编程语言里还承担"转义字符"的角色。你想写一个 Windows 路径,可能要写成这样:
java
String path = "C:\\Users\\rock\\Downloads";
什么,你不信?我给你看看我的 Android Studio 配置的 sdk.dir:
toml
sdk.dir=C\:\\Users\\rock\\AppData\\Local\\Android\\Sdk
明明只是一段文件路径,却像在给字符串做针灸。
我们在开发的时候,稳妥一点的写法通常不是手搓分隔符,而是交给语言或标准库处理。
Java 里有 File.separator、File.separatorChar,更现代一点可以用 Path.of(...)、resolve(...);.NET 里有 Path.DirectorySeparatorChar、Path.Combine(...);很多构建工具也会自己处理平台差异。
那么,问题来了:为什么一开始就要有这个差异?这两系统就不能用同一个分隔符吗?为什么 Linux 和 macOS 用 /,Windows 却偏偏用了 \?
回答这个问题,对比的不是"谁更先进","谁故意不兼容谁"的问题了。
它更像一段软件史里的连续剧:Unix 做了一个简洁选择,DOS 背着一个早期包袱,Windows 又把这个包袱一直延续了下来。
现在,让我们拨开重重迷雾,回到那个早期的 Unix 系统上来。
Unix:/ 是一棵树的根
今天,一提到 Linux 的路径,我们几乎都会想到 /。
text
/home/panoo/project
/etc/hosts
/var/log/nginx/access.log
但你知道吗,/ 本身有两个含义:
/是目录之间的分隔符;- 单独一个
/表示整个文件系统树的根目录。
早期的 Unix 文件系统就像一棵树。
树有根,根下面有目录,目录下面又可以挂文件和目录。路径就是从根一路走到目标节点的路线。于是 /usr/bin/env 读起来就像:
text
从根目录 /
进入 usr
再进入 bin
找到 env
这种模型简单直接,历史的长河也证明了,它活得最久。
当然,至于这个符号的选择,也颇有趣。
Unix 操作系统的前身是 Multics 操作系统,Multics 系统已经有层级文件系统的思想。不过,当时的 Multics 使用 > 作为路径分隔符,比如类似 dir>subdir>file 这样的形式。
正常情况下,Unix 应该使用 > 作为分隔符才对,但是,好巧不巧!Unix 的设计者早在实现多级文件系统之前,就已经把 > 和 < 预留给 Shell 命令行的输入和输出重定向功能了。
这个大家一定不陌生,因为今天还在这么写:
bash
make xxxx > log.txt
command < input.txt
既然 > 已经有活干了,目录分隔符就得另找人。找谁呢?看看我们的键盘:

> 的右边是 /,那就它了,不为别的,离得近啊!
哦,对了,/ 还有一个很有意思的副作用:根目录本身没有名字。
普通目录都有名字,因为它们存在于父目录中。比如 /home 里的 home,是根目录下面的一个条目;/home/panoo 里的 panoo,是 home 下面的一个条目。
但根目录没有父目录。它不是"某个目录里的一个名字",它就是整棵可见文件树的边界,顶层。所以用一个分隔符 / 来表示它,干净!
text
/ 根目录
/home 根目录下面的 home
home 当前目录下面的 home
注意,这个简洁,干净,颇具美感的表示方法,很容易让初学者踩一个坑 ------ 路径是否以 / 开头,直接决定它是绝对路径还是相对路径。
text
/tmp/a.txt 从根目录开始找
tmp/a.txt 从当前目录开始找
这种路径的表示方法,和现代的 Windows 有根本的区别,它没有复杂语法,没有盘符,没有卷名,没有"这个路径到底从哪开始"的额外解释。一个 / 字符,既能分隔目录,又能表达"从根开始"。
这大概就是 Unix 风格最迷人的地方:在历史偶然的促进下,形成了一种稳定、简约的约定。
/ 走出 Unix

Unix 的 / 后来没有只留在 Unix 世界里。
Linux 当然继承了这套路径风格。MacOS 也属于 Unix 传统的一部分,现代 MacOS 甚至可以在 The Open Group 的 UNIX 认证产品列表里找到。
你在 MacOS 终端里写:
bash
cd /Applications
cd /Users/yourname
这和在 Linux 里是一模一样的!。
Web 世界也选择了 /,我们每天看到的 URL:
text
https://example.com/a/b/c
本质上也在表达一种层级结构。RFC 3986 对 URI 的路径部分有明确描述:path 由一系列 path segment 组成,segment 之间用 slash / 分隔。
所以 Web 开发者的肌肉记忆也被 / 塑造了:
text
/api/users
/static/images/logo.png
/posts/2026/05/path-separator
这就是为什么很多人在第一次跨平台工作的时候,碰到文件路径时那种别扭的原因。
Unix、Linux、MacOS、URL、容器里的路径、CI 里的脚本、服务器上的日志目录,它们共同构成了一个巨大的 / 文化圈。
然后 Windows 站在旁边说:我不。
Windows:臣妾做不到啊!

要理解 Windows 为什么用 \,从 Windows 讲是不行的,我们得先把时间拨到 DOS 的年代。
更准确地说,要从 MS-DOS 1.x 开始。
早期 DOS 很简单,主要面向软盘。当时 DOS 1.0 甚至还没有层级目录结构。磁盘上就是一堆文件,没有今天这种目录套目录的树。
但 DOS 已经有命令行参数了,而且它用 / 来表示命令开关。比如后来我们熟悉的:
bat
ipconfig /all # 我相信这个命令,大部分开发者都敲过
dir /w
dir /p
copy /b a.txt b.txt
format /s
这里的 /w、/p、/b、/s 都不是路径,而是告诉命令"用某种方式执行"。
这个习惯很早就存在。Larry Osterman(1992 年就入职的微软资深工程师)在微软博客里回忆,DOS 1.0 时代很多工具使用 / 作为 switch character,也就是命令开关字符。
这种风格可能和 DEC 系统传统有关,比如 TOPS-10、VMS 等系统里 / 常用于命令选项。
看到这,各位可能一下就明白了,这就是关键冲突了!
后续的 MS-DOS 2.0,IBM PC/XT 带来了硬盘,硬盘一大,单层文件列表就不够用了。于是 DOS 需要支持层级目录。既然要支持层级目录,就必须选一个路径分隔符。
Unix 的 / 看起来当然很香。可问题是:/ 已经被 DOS 命令行拿去做开关了。
如果强行把 / 改成路径分隔符...来,我们想象一个旧批处理脚本里有:
bat
dir /w
原本意思是:用宽格式列出当前目录。
如果 / 突然变成路径分隔符,解释器就可能开始琢磨:这是根目录下面的 w?还是命令参数?
旧脚本、旧工具、旧用户习惯,全都可能出问题。
当然,如果我们以今天的视角回头看,会觉得"为了一个短命的早期 DOS 兼容性,牺牲几十年的路径统一,值吗?"
但站在 1980 年代早期,没人知道未来会变成这样。
那时 Unix 不一定会统治服务器,DOS 也不一定会走进千家万户。工程师眼前最现实的问题是:新系统不能把已有用户的命令、工具、批处理全弄坏。尤其是商业系统,兼容旧业务才是重中之重!
于是 / 不能用!
此刻的 Windows 可能还没有意识到,它做出的这个选择,让软件世界错过了世界大一统的机会。
那用什么?
Larry Osterman 给出的说法很朴素:选了"下一个最好的字符"。
也就是视觉上和 / 很像的 \。
text
/ forward slash
\ backslash
一个向前倒,一个向后倒。恰似一对因为历史原因分道扬镳的兄弟。
DOS 并不是完全不认 /
如果故事到这里结束,那 Windows 的行为就很好理解了:
Unix 先用了 /,DOS 的 / 被命令开关占了,所以 DOS 用 \,Windows 继承 DOS。
但现实总是更拧巴一点。
DOS 和 Windows 的底层 API 很多时候其实也接受 / 作为路径分隔符。
比如在不少 Windows 环境里,你可以写:
text
C:/Users/rock/Downloads
很多 API 能正常处理它。微软的 .NET 文档也明确说,Windows 支持 / 和 \ 作为路径分隔符:Path.DirectorySeparatorChar 是 \,Path.AltDirectorySeparatorChar 是 /。Windows 在规范化路径时,还会把 forward slash 转成标准的 backslash。
但"底层支持"和"生态统一"不是一回事。
命令行程序会自己解析参数。老工具有老工具的规则。某些 UI、某些脚本、某些第三方程序,未必都愿意或能够接受 /。更别说 Windows 还有 \\server\share 这样的 UNC 路径,以及 \\?\C:\... 这种绕过普通路径规范化的特殊前缀。
所以在 Windows 世界里,/ 有点像一个会被很多地方理解的外来语;而 \ 才是本地口音。
有时候你用 / 系统也能听懂,但你要写正式文件,最好还是用 \,这才是本地规范。
曾经有过"改回去"的机会吗
有,而且还挺有戏剧性。
好吧,料想 Windows 也不会如此硬气。
Larry Osterman 的文章提到,DOS 开发者其实并不完全满意这个状态。毕竟他们也熟悉 Xenix 和 Unix 风格。于是 DOS 里曾经有过一个机制,可以改变命令开关字符。
比如把命令开关从 / 改成 -,路径就可以更接近 Unix:
bat
dir -w
cd /users/rock
额...更有意思的事情时,DOS 里曾经还有 SWITCHAR= 这样的配置,用来改变 switch character。Retrocomputing 上有个讨论提到过,DOS 2.0 之后的系统调用层面,路径名里用 \ 或 / 分隔,在很多场景下都能工作。
但这条路没有成为主流。
原因实际上很简单:兼容性、文档、用户习惯、厂商工具、批处理脚本,它们一旦形成合力,就会比单个技术选择更强。
软件世界里有一种东西叫"默认值的重力"。
默认值一旦被写进文档、教程、书籍、工具输出、用户记忆,再想改,就不是改一个字符,而是改变一整片生态。
Windows 最后留下来的默认值就是 \。
一个字符,怎么影响了这么多人?
路径分隔符看起来是小事,但它造成的影响一点都不小。
在 C、C++、Java、Kotlin、JavaScript 等语言里,\ 常常是转义字符:
text
\n 换行
\t 制表符
\\ 一个真正的反斜杠
所以 Windows 路径进到字符串里,就经常要双写:
java
String path = "C:\\Users\\rock\\Downloads\\demo.txt";
否则 \U、\D、\n 之类的组合可能会被语言解释器当成奇怪的转义。
如果你稍微研究过正则表达式,那就更刺激了,因为正则本身也爱用 \。如果你在 Java 字符串里写正则,又想匹配 Windows 路径,反斜杠会开始套娃。
这也是为什么跨平台代码里,直接拼字符串通常不是好习惯。更推荐的做法是:
java
Path path = Path.of("logs").resolve("app.log");
或者:
java
Path path = Paths.get("logs", "app.log");
你需要表达的是"目录 logs 下面的 app.log",而不是在"操作系统喜欢哪个字符"上去下赌注。
这正是高级语言和标准库存在的意义:把历史坑封装在 API 后面,让业务代码少关心具体实现,抽象才是关键!
斜线背后的工程命运
Unix 的 / 像一个漂亮的极简设计。
它从层级文件系统出发,统一了根目录、绝对路径和目录分隔符。它简单、稳定、容易组合。后来 Linux、macOS、Web URL 又把它带到了更广阔的世界。
Windows 的 \ 则像一个典型的兼容性选择。
它不是一开始就为了"与众不同"而存在,而是在 DOS 2.0 增加层级目录时,面对已有 / 命令开关传统做出的妥协。它保住了早期用户和工具,却也把一个视觉上只差一点点的分歧带进了几十年的软件生态。
一个代表简洁模型。
一个代表历史债务。
但说到底,它们都不是纯粹的技术审美题。
软件工程从来不是在真空里寻找完美答案。它要面对已有用户、已有代码、硬件键盘、命令行习惯、文档体系、商业伙伴、迁移成本,以及那些当时看起来很小、后来却变成地层的选择。
我们当然可以惋惜:如果当年 DOS 没有把 / 用作命令开关,世界会不会少很多 C:\\Users\\...?
也可以反过来想:如果每一代软件都为了"更漂亮"而轻易打破兼容性,今天的很多系统可能根本活不到今天。
所以最后我更愿意把这两个符号看成软件工程的标本。
/ 告诉我们,简单一致的抽象能走很远。
\ 告诉我们,真实世界里的系统会背着历史继续前进。
软件工程也是一个妥协的系统工程。世界不存在统一的、完美的解决方案。我们在旧约束里修新路,在新问题里背旧债,在矛盾中继续前进。
也许这正是软件发展最有意思的地方:它不完美,但它活着;它不统一,但它持续演化。
两条斜线,一个向前,一个向后。它们都在带我们往前走。
Reference
- Why is the root directory denoted by a
/sign? - Unix & Linux Stack Exchange - A General-Purpose File System For Secondary Storage
- Why is the DOS path character
\? - Larry Osterman / Microsoft Learn Archive - Why Does Windows Really Use Backslash as Path Separator? - OS/2 Museum
- Slash versus backslash as directory separator - Retrocomputing Stack Exchange
- File path formats on Windows systems - Microsoft Learn
- Path.AltDirectorySeparatorChar Field - Microsoft Learn
- File (Java SE 26 & JDK 26) - Oracle Docs
- RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
- The Register of UNIX Certified Products - The Open Group