文件路径二三事:为什么“斜杠家族”能笑到最后
文件路径(file path)广泛用于表示操作系统中的目录/文件关系,也是用于确定系统中文件位置的方式之一。
现如今,常用的文件路径表示方式有两种:Unix & 类 Unix 使用“/(正斜杠/撇斜杠)”分隔路径,DOS[1] 以及后来的 Windows 则默认使用“\(反斜杠/捺斜杠)”。
然而疑问就来了:
- DOS & Windows 与 Unix 并非“师出同门”,为何也选择了“斜杠”?
- 许多情况下 Windows 也支持“/”分隔路径,那么一开始选择“\”的目的何在?
- 除了斜杠,历史上有没有出现过使用其他符号的路径表示方式呢?
带着这些疑问,下文就来回顾文件路径的演变历程,也算是从一个侧面窥见操作系统的发展史以及不同操作系统间的相互影响。
1.Multics:“>”
一般认为第一个引入分层文件系统的操作系统是 20 世纪 60 年代的 Multics,它由麻省理工学院、贝尔实验室和通用电气公司设计。
Multics 选用了“>”表示(相对工作目录的)下一级目录,“<”用于表示(相对工作目录的)上一级目录(常用于表示相对路径),模拟箭头的形状(→/←)(在我看来是最形象的),如:
假设工作目录为:>udd>Demo>JQUser
绝对路径:>udd>Demo>JQUser>subdir>myfile;>udd>NewDemo>JQUser
相对路径:subdir>myfile;<<NewDemo>JQUser
同时,也有资料(参考资料4)显示,在开发早期,Multics 曾选用过“:(冒号)”作为路径分隔符。
(巧合的是,现在常用于表示路径的"/""\"正好像是把">"分成了两半,也算是对前辈的一种致敬吧)
2.Unix:“/”
1969年,在贝尔实验室工作的 Ken Thompson 和 Dennis Ritchie 在一台 DEC 小型计算机 PDP-7 上编写了 Multics 的改编版,这就是后来的 Unix。
Unix 继承了 Multics 包括分层文件系统在内的许多设计,但在路径表示上,Unix 把 Multics 的“<”“>”换成了“/”。
替换“<”“>”的原因是“<”“>”在 Unix 中作为输入/输出重定向(将命令的输入和输出从默认的设备(通常是键盘和显示器)重定向到其他地方)字符。
而换成“/”的原因可能是因为在 20 世纪 70 年代常用的 Lear-Siegler ADM-3A 终端键盘(以及现在的许多键盘)中,“/”就在“<”“>”的旁边,且不需要按住“Shift”,可直接打出。
Unix 以“~”表示当前用户的主目录也与键盘有关,因为终端键盘的波浪线符号和“Home(位于键盘右上角)”一词恰好位于同一个按键上。
文件路径分隔符的一个重要特点(或者说,原则)是:该符号能保证不需按下“Shift”键,可直接打出。
那可能有人会问了:那上文说的 Multics 呢?我看键盘上“<”“>”在按键上位,需要按下“Shift”键才能打出啊?
针对这个问题我去查了一下,结果发现,Multics 第一个采用的打字机 Teletype Model 37,长这样:
“<”“>”分别在键盘左上角和“TAB”键旁,且都独占一个键位,不需按下“Shift”键才能打出。
3.<DEC>[VMS]:Mac……
在介绍 DOS&Windows 前,先来看看其他操作系统的路径表示方式。
除了 Unix 的“/”之外,当时的其他操作系统也有自己独特的路径表示方式。
与 Unix 相比,DEC 操作系统的路径表示略显复杂。1967 年 DEC(Digital Equipment Corporation,数字设备公司)推出的 TOPS-10 使用 [] 包裹目录,以“,”分隔目录,且目录标在文件名后面(个人觉得这有点不太符合人的操作习惯,毕竟操作系统中目录与文件的关系应是“目录→文件”),如:
DSKC:MYFILE.TXT[21,589]<055>(依次为:设备名:文件名.文件扩展名[目录]<保护代码>)
后来的 DEC TOPS-20(虽然都叫 TOPS,但其与 TOPS-10 几乎无关)则采用 <> 包裹目录,以“.”分隔目录,如:
PS:<USER.DOCS>LETTER.TXT,4(依次为:盘符:<目录.子目录…>文件名.文件扩展名,文件版本)
OpenVMS 使用 [] 包裹目录,以“.”分隔目录,如:
Stromasys001::dka100:[sys0.main]abc.txt;1(依次为:主机名::盘符:[目录.子目录…]文件名.文件扩展名;文件版本)
苹果曾尝试过多种路径表示方式。Lisa OS(1983 年)使用“-”分隔路径,ProDOS(1983年推出,在 Apple II 上运行)使用“/”,Classic Mac OS 则使用“:”,GS/OS(1988年推出,在 Apple IIGS 上运行)同时支持“/”和“:”。
后来苹果推出的 Mac OS X(2000年)尽管类似 Unix,采用了“/”的表示方式,但为了保证与 Classic Mac OS 的兼容性,一些情况下文件系统会将文件名中的“:”替换为“/”。
1987 年推出的 RISC OS(在 ARM 芯片上运行)则采用如下方式[2]:
[文件系统:][:驱动器号或磁盘名称.]$.[目录].[文件名]/[文件扩展名]
如:ADFS::MyRiscPC.$.Documents.Letter (filetyped to text)
LanMan::WindowsC.$.Pictures.Example/gif
4.DOS[3] & Windows:“\”
随着 MS-DOS 2.0 的推出,微软在为 MS-DOS 引入目录设计的同时,选用了一个不一样的路径分隔符:“\”。
既然选了“斜杠”,为什么不用“/”?因为“/”在 DOS 中已被用于选项字符(switch character,用于分隔命令选项参数的字符,如 dir /s,而 Unix 则采用“-”(这也是源于 Multics))
那为什么 DOS 的选项字符用的是“/”呢?这一点有不同的说法。
微软软件工程师 Larry Osterman 在文章中写到,这与当时 DEC 操作系统的操作习惯有关。
许多 DOS 实用程序都是由 IBM 编写的,它们使用“/”字符作为实用程序的选项字符(这是用于分隔命令行参数的字符,在 *nix 上是“-”字符,在大多数 DEC 操作系统(包括 VMS、DECSystem-20 和 DECSystem-10)上是“/”(注:我不确定“/”字符是来自 IBM 还是来自微软——最初的 MS-DOS 开发人员中有几个是 DEC-20 的老手,所以他们可能是从 DEC 中继承的)。
下图为 Microsoft BASIC-80 的部分命令,其使用“/”作为选项字符。
微软的《MS-DOS 百科全书(1988 年)》也提到,微软早期员工 Marc McDonald 开发了一种名为 M-DOS 的 8 位多任务操作系统,该操作系统“以 DEC TOPS-10 操作系统为蓝本”。此外,该书还明确指出:
MS-DOS 1.x 版借鉴了 DEC 操作系统的传统,在命令行中使用了“/”作为选项字符。
此外,还有人认为,这与 CP/M 有关。对此程序员 John Elliott 做出了澄清。
1.CP/M 本身没有“选项字符”的概念。与 DOS 不同,没有系统调用来获取要使用的正确字符(参见 INT 21h/AX=3700h)。每个程序使用的字符由该程序定义。UNIX 也是如此。但与 UNIX 不同的是,CP/M 中不同程序的作者选择了不同的字符。
2.与 DOS 不同的是,系统不会强制执行文件名是否有效。在 CP/M 2 中,没有调用将文件名转换为 FCB,因此每个程序必须执行自己的操作;CCP 将两个解析后的文件名传递给程序,但程序无法调用其解析器,因此任何其他名称都必须由它们完成 - 不一定遵循相同的规则。
3.由于不同程序对哪些字符可以/不可以出现在文件名中没有一致的规定,因此选项字符也是不一致的(因为选项字符不能出现在文件名中是有意义的) 。
然而,有人在 CP/M 3(通常称为 CP/M Plus)用户手册上发现,“/”被规定为“选项分隔符”。然而,CP/M Plus 于 1983 年发布,而在 MS-DOS 最初开发时(1981年)对应 CP/M 最新版本是 CP/M 2,而我并未找到 CP/M 2 及之前版本“/”被规定为“选项分隔符”或“/”不能用于文件名的相关信息。
注:CP/M 2.2 用户手册确实有“/”不能用于文件名的说明。然而,该手册在 1982 和 83 年做过更新,所以无法确定在 82 年前 CP/M 2.2 是否也遵循这一规定。而且,根据 John Elliott 的分析,即使有这一规定,程序本身也并不一定遵守。
那么问题又来了:既然微软与 DEC 有这么多的联系,那为什么文件目录没有依照 DEC 操作系统的风格,而是采用了 Unix 的路径表示风格呢?
对于 MS-DOS 2.0,DOS 的设计者选择了混合版本 - 他们已经支持 DOS 1.0 的驱动器号,因此他们需要继续使用它。他们选择使用 *nix 风格的方法来指定层次结构 - 而不是(如 VMS 和 DEC-20)在文件名中调用目录。
下图是微软于 1980 年发布的 Unix 操作系统:Xenix。在 20 世纪 80 年代中后期,Xenix 已是最常见的 Unix 系统(根据安装它的机器数量来衡量)。就连当时微软的 DOS 开发人员平时用的也是 Xenix 机器,也因此他们对 Unix 命令和语法十分熟悉。
可能也正基于此,微软采用了 Unix 的路径风格。而微软本来希望使用 Unix 形式的“/”作为路径分隔符,但 IBM 不同意,因为这会导致与已采用“/”作为选项字符的 DOS 1.x 的不兼容。
当然微软还有其他的选择,比如 DEC 风格的“.”形式,然而这又与分隔文件扩展名的“.”冲突(要么就得像后来的 RISC OS 那样替换“.”)
综合考虑了键盘布局(比如,要保证这个符号不需按 Shift 键才能打出)等因素,也可能带有与 Unix 分庭抗礼的意味,微软最终选用与“/”形态相似的“\”作为路径分隔符。
不仅如此,为了兼容 Unix 语法,微软不仅对操作系统进行编码以同时接受 / \ 作为路径字符,甚至还在 config.sys 添加了一个配置选项 SWITCHAR,可以将选项字符更改为“-”。
不过,微软可能并没有经过深思熟虑就选择了 \,因为根据现有资料看,微软当时就是想选择 / 作为路径分隔符的,后来是根据 IBM 的要求才进行了修改,改的时候也比较匆忙,因为当时用户手册等配套文件都写好了,突然说要改,连用户手册都来不及修改,只能临时加了一条注释(可参见MS-DOS 发行说明 - GitHub)。如果是这样仓促的情况,很可能当时微软并没有考虑很多,只是看 \ 长得像 / 就选择了 \ 。
(附注释原文节选:用户手册包含一些重大错误。其中大部分是由于最后一刻的更改而导致的,目的是实现与 IBM 的 MS-DOS(PC-DOS)的更高程度的兼容性。这包括使用“\”而不是“/”作为路径分隔符,以及使用“/”而不是“-”作为选项字符。对于跨机器传输批处理文件,微软鼓励在美国市场分别使用“\”和“/”。)
目前,Windows 仍支持 / \ 两者作为路径分隔符,但仍有一些情况只支持 \。
而现在看来,微软最初选择 \ 也并不十分高明。因为 \ 也用于转义字符,因此在编程等使用环境下可能会导致一些不必要的麻烦。但换个角度看,选其他符号(尤其是已经出现在键盘上的这些符号)也还是会有其他的冲突,所以也很难评价选其他符号就会比 \ 来的更好。
关于“\”
其实在当时,“\”算是一个比较新的符号,在 20 世纪 60 年代前还十分少见。
直到 1960 年 6 月,IBM 发布了“扩展字符集标准”,其中才有了“\”的影子(位于 0x19 处)。1961 年 9 月,IBM 的 Bob Bemer 向 X3.2 标准委员会提议将“\”纳入拟议标准,以支持 ALGOL 编程语言的逻辑运算符(/\,表示“与”)和(\/,表示“或”)。该提议后来得到了采纳,“\”进入了 ASCII,Bob Bemer 也因此被称为“反斜杠之父”。
“\”进入标准字符集后,为了与出现时间较早的“/(slash,斜杠)”做区分,“\”被称为“后斜斜杠(backslash)”或“反向斜杠(reverse slash)”,国内为了更好区分两者也有用“撇(/)捺(\)斜杠”称谓的。
而在日文/韩文版的 Windows 中,“\”会显示为各自的货币符号(¥/ ₩)。这是因为,ASCII 制定后的 ISO-646 标准将 0x5c 定义为“可本地化”代码点之一,顾名思义,这个点位可以由不同国家或地区根据需要显示为不同的字形。于是,在 Shift_JIS 中,0x5C 用于表示日元符号;在韩语字符集中则是韩元符号。即使到后来系统已经支持 Unicode 编码,为了保证兼容性,微软依然选择货币符号作为 0x5c 的日韩文字符映射。
注:Shift_JIS 最初基于的 JIS X 0201 早在 1969 年制定,那时候的电脑尚不具备处理双字节文字的能力,所以只能先引入半角的片假名(参考 PC-88 早期、夏普 MZ 等日系机),但代价就是,单字节码位不够了,于是就替换了一些当时还不常用的符号,比如日元符号 ¥ 就取代了 \。
总结
文件路径分隔符的选择看似简单,但其中需要综合考虑字符本身的特性、键盘布局、该字符的其他功能是否与路径表示冲突等因素,操作系统设计的复杂程度可见一斑。而且,从路径表示的发展历程中,我们也可以窥见不同的操作系统是如何互相影响的。
或许现在看来,Unix/Windows 等选用的单字符路径表示方式是最简单的。虽然不能说这样的路径表示是他们得以普及的原因,然而这背后体现出的易用性和较低的学习成本,在计算机向大众普及的过程中也显得不可或缺。(试想一下,如果 Windows 采用的是 DEC 的路径表示,结果又会如何?)
参考资料
- en.wikipedia.org/wiki/Path_(computing)
- betawiki.net/wiki/Multics
- web.mit.edu/multics-history/source/Multics/doc/info_segments/pathname.gi.info
- www.multicians.org/fjcc4.html
- multicians.org/mgt.html
- StackExchange - Why is the root directory denoted by a / sign?
- StackExchange - Why did Unix use slash as the directory separator?
- TOPS-10 Operating System Commands Manual
- TOPS-20_User's_Guide
- StackExchange - Why did MacOS Classic choose the colon as a path separator?
- Apple Lisa Operating System Reference Manual (PDF, 1983)
- Apple II GSOS Reference Volume 1.pdf
- RISC OS Filename Translation
- Larry Osterman - Why is the DOS path character 「\」?
- OS/2 Museum - Why Does Windows Really Use Backslash as Path Separator?
- www.seasip.info/Cpm/optchar.html
- www.cpm.z80.de/manuals/cpm13int.pdf
- www.cpm.z80.de/manuals/CPM_SummaryGuide.pdf
- en.wikipedia.org/wiki/Xenix
- HOW ASCII GOT ITS BACKSLASH - By Bob Bemer
- StackExchange - Why is backslash called BACK slash when arguably it points forward?
- At long last, explaining the yen/won/backslash bug plausibly