Why does Windows use backslash as path separator? (2019)
os2museum.com
os2museum.com
I realize this is from the OS/2 Museum and not Microsoft - but the result is the same:
Article written about a poor engineering decision made decades ago, which for reasons that perplex everyone still live-on today. Then the Microsoft apologists come out of the woodwork to explain how easy it is to live with these poor engineering decisions and how smart Microsoft Engineers are, and how it's literally all other operating systems that somehow are lacking, blah blah blah.
Backwards compatibility is usually the smoke screen apologists deploy during these discussions. As-if in the past few decades Microsoft couldn't have written a simple '\' -> '/' converter... Hell, if you look in the Explorer address bar, Microsoft even hides the path separator - opting to replace it with arrows instead.
Let's not even start on the CRLF thing...
Yes, Windows owes it's origin to OS/2, and OS/2 used '\' instead of '/' - so we should never correct a decision inherited from an OS that died 22 years ago...
Win32 file APIs can actually use either '/' or '\\' as a path separator. The stuff this is talking about is mostly around DOS command line tools and the successor, cmd.exe, which yes is mostly meant to run scripts written for DOS.
'\\' was selected by DOS, specifically DOS 2.0 per TFA, several years before OS/2 existed.
> Win32 file APIs can actually use either '/' or '\\' as a path separator.
Fantastic - so why are we still entertaining this idiotic file separator everywhere within the OS itself?
> '\\' was selected by DOS, specifically DOS 2.0 per TFA, before OS/2 existed
Fine - so why the hell are we still doing things like a dead, defunct OS did and literally nobody else did because, you know, it was unintelligent?
Dude, why are you so angry about what direction a slash goes?
Once they introduced the ability to use either one, they still had a lot of software that hardcoded '\\', including lots of their own. Again, they support either one in most places. The backslash is considered more canonical because it came first.
Going back to DOS for a second, TFA even says Microsoft wanted to use '/' in DOS 2.0 when they introduced directories (1983), but IBM objected.
When Microsoft wrote IIS and other web technologies the internal friction of path separators must have been fun to experience. "Why are we still using \ again, Jim? Oh, that's right, we chose poorly a decade ago and for some reason can't be bothered to fix it!"
> Dude, why are you so angry about what direction a slash goes?
Because it's rubbed my bum raw over the years and I've finally had enough! I mostly jest... but seriously, it's super annoying to deal with and a constant reminder of the poor decision making Microsoft products often encompass.
Microsoft just chooses to still display \ to you in the UI... which is ridiculous and perpetuates \ living on in all user-facing places.
I personally like the latter but it's a matter of taste I guess :-). It gives me warm and fuzzies to see C:\ D:\ etc. live on.
Note also that Windows tends to use 16 bit Unicode everywhere, that's probably a bigger thing for IIS than the path separator.
Saying it was the wrong choice is simply revisionist history.
You are, unintentionally, being the very sort of apologist my post above complained about.
As TFA says it wasn't clear yet that unix would be so dominant. Also, a lot of these systems were introducing the concept of directories for the first time in their history. That's how old we're talking. That directories are novel to some people at that point.
Microsoft wrote a 'converter'. Terminal, Explorer, and other applications recognize "/" and convert it to "\". If it takes both, it doesn't matter which one is "real".
Hint: the post you are replying to is humorous and the tone should be read as sarcastic.
Firstly, the choice of backslash in DOS 2 didn't enable backwards compatibility with anything because nothing prior to DOS 2 used the backslash character for this.
Secondly, I do not see any basis to say that somehow MS looked forward in time, at the future, and chose a character unused by any other OS.
As early-1980s MS did not have a time machine and thus was not looking forwards, then how did that choice help future compatibility? How was the use of backslash better for Windows for Workgroups 3.11 (because that was the first one with its own VFAT filesystem that didn't just call DOS) and subsequent releases? How was a backslash preferable to anything else?
How would using any other character be worse?
Would choosing a character used by other OSes not have been _better_ for long term compatibility, aiding source code conversion and porting?
I really do not see what you are trying to get at.
Incidentally, I entirely agree that its compatibility record is astonishingly good, but I can't see any link from that to the choice of directory delimiter.
> How was a backslash preferable to anything else?
Because it existed prior to Windows 1.0 and beyond application compatibility, you also had to work with user familiarity. Heck, it is annoying enough to swap between macOS and Windows for Ctrl/Cmd-C and Ctrl/Cmd-V, or even within macOS between Mac applications and the Terminal. Or my worst personal enemy, Ctrl-H/Cmd-H (browser History vs. hide window) in a browser. Arg!
> Would choosing a character used by other OSes not have been _better_ for long term compatibility, aiding source code conversion and porting?
Which OS should they have chosen from? [0] Apple SOS [and all future versions of macOS]? VMS/OpenVMS? RiscOS? Domain/OS? TOPS/20? Stratus VOS? What made SysV or BSD special or 'a clear choice' in path separators in the '80s?
> How would using any other character be worse?
I don't believe any other character would be better or worse at the time backwards compatibility was ruled supreme at Microsoft. Since their operating systems already used the backslash character, and the preceding mention of user familiarity, and of course the code base is DOS which they probably didn't want to re-write due to effort and cost, they stuck with it.
I certainly see a point in changing the path separator if you pulled a Mac OS Classic to Mac OS X style switch, but Microsoft never really did that. Yes, they had NT, but that needed to have compatibility with it's DOS-based sibling as it could also run DOS applications.
Oh imagine the threads if they did switch to the forwardslash in Windows... "What else from Linux will Microsoft copy?!" "Did Microsoft switch to the Linux kernel?!". It would make for an exciting day in tech!
I'm certainly not against Microsoft changing the path separator to the forwardslash.
Nobody is doing it except the people using cmd.exe over Windows Terminal or Powershell. If you're using a DOS terminal you're gonna get DOS results.
I get around by using Git Bash (which is actually quite good for being a bundled afterthought), cygwin, or lately even WSL containers.
It is pretty powerful/insane that stuff like this is a part of PowerShell and that's probably what the GP is talking about:
Add-Type -TypeDefinition @"
using System;
namespace DemoCode
{
public class DemoClass
{
public static void WriteHello()
{
Console.WriteLine("Hello");
}
}
}
"@
[DemoCode.DemoClass]::WriteHello()
Or LoadLibrary call and invocation from PowerShell. [0] [1]It blurs the lines as to what it is no matter how you look at it.
In contrast, try doing that in cmd.exe or invoking a function from a shared object after getting a pointer to it using dlopen through a POSIX shell.
[0] - https://www.powershellgallery.com/packages/PSReflect-Functio...
Command lines, graphical shells, OS save and open file dialogs do not care if you use slash or backslash, and the Win32 API has never enforced backslashes as a path separator.
Yes, it's true modern languages have things like `Path.separator` and stuff to abstract this away, but you cannot always use them in all contexts.
It's small friction that adds up to a sore bum after a while...
[1] https://retrocomputing.stackexchange.com/questions/7030/why-...
(I don't personally like powershell but I don't think my gripes with it are really relevant to this thread.)
On Unix, you create a process with fork() and execve(). Execve takes a list of arguments, already parsed and tokenized. A shell is free to interpret the command line however it likes to build this, but at the syscall level it is completely unambiguous what the parsed array looks like.
On Windows, you call CreateProcess. This takes the command line as an unparsed string. It is the child process's responsibility to chop it up into argv. This is the source of ambiguity that I'm talking about.
What you're talking about is related, but different. You're saying that Windows forces an unnecessary join and split (with all the complex rules required to properly parse a quoted string) when using the Windows equivalent of exec(). which is inelegant, but unrelated to what I'm describing above (what I describe above differs because of how newlines and spaces are treated as delimiters (IFS)).
Further, from the testing I did, there is no noticeable impact on my C application which reads from argv- the C runtime seems to handle this transparently.
I disagree with your assessment here.
First, the C runtime doesn't have the "part of the system" feel that it does in the Unix world. There is a CRT in the OS, but most programs link to the CRT that ships with visual studio, so it could theoretically change version to version, compiler to compiler.
Apart from that a Win32 application could choose to bypass the CRT's entry point and use WinMain(). Many do. Even if it does use CRT main there is GetCommandLine() to reach the raw command line underneath. Shell32 even has a function CommandLineToArgvW which behaves differently from CRT. Those two are different from parsing done by cmd.exe. There is a lot of ambiguity is my point.
I picked the first MSFT page on argv, https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-c... and compiled and ran that program with various command lines, and it does exactly what I expected (argv array members are tokenized according to my quoting).
Could you be clearer, perhaps by posting a simple C example and a few command lines demonstrating what you mean?
https://github.com/rust-lang/rust/issues/44650
This in addition to the Rust CVE mentioned elsewhere in the thread which was rooted in this issue:
https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
Here are some quick programs to test contrasting approaches. I don't have examples of inputs where they parse differently on hand right now, but I know they exist. This was also a problem that was frequently discussed internally when I worked at MSFT.
#include <stdio.h>
int main(int argc, char **argv)
{
for (int i=0; i<argc; ++i)
puts(argv[i]);
return 0;
}
#pragma comment(lib, "shell32.lib")
#include <windows.h>
#include <stdio.h>
int main()
{
PCWSTR cmdLine = GetCommandLineW();
if (cmdLine)
{
int argc = 0;
PWSTR *argv = CommandLineToArgvW(cmdLine, &argc);
if (argv)
{
for (int i=0; i<argc; ++i)
printf("%ls\n", argv[i]);
LocalFree(argv);
return 0;
}
}
return 1;
}Technically yes, but practically the only correct way to tokenize these command line strings is CommandLineToArgvW WinAPI implemented by Shell32.dll.
cmd.exe is an important and terrible example:
https://learn.microsoft.com/en-us/windows-server/administrat...
From the part of Microsoft that was focused on cleaning up the windows code base in that era, the shell was not considered a core component. All of the utility functions that it provides like this were considered baggage, improper layering, poor design.
Windows predates OS/2 (OS/2 development started in 1985 according to Wikipedia)
How is it a poor engineering decision?
What about the original Mac OS using colon as a path separator? Or Lisa OS using the hyphen as a path separator?
see https://retrocomputing.stackexchange.com/questions/17069/why...
That's hardly uncommon! Examine the history of basically anything.
It isn’t a poor engineering decision, that guy just didn’t read the decision criteria.
Also, according to spec, CRLF is the proper sequence for a newline, and both Unix and MacOS chose incorrectly.
CRLF existed originally because of mechanical typerwriters required two separate actions - Carriage Return (move the carriage back to the start of the line), Line Feed (advance the page to the next line).
CR is a waste for computing... it literally does nothing beneficial.
Someone should figure out the climate impact of decades of wasted processing of CR characters... that might motivate people to fix this as well.
LF vs CRLF is a single molecule of water compared to the seven seas of waste created by Electron apps or even just JavaScript itself. Todays developers are lazy as FUCK and there is no pressure for them to stop being lazy.
See: https://tonsky.me/blog/js-bloat/
> CR is a waste for computing... it literally does nothing beneficial.
When “LF” means “line feed and carriage return” both, as it does thanks to UNIX, yes. On teletype machines “line feed” meant “line feed” and “carriage return” meant “carriage return”. You need both.
Today we use an LF which means both “CR” and “LF” and we act like CR is the problem, when it was us choosing a single byte to mean two separate things joined together.
Even as a kid in school in typing class, I used a typewriter with a parallel port (probably serial, really, because you could use the keyboard without putting text on paper), and it could be used to print text sent to it. CR and LF had very different meanings on that typewriter. I wish I remembered what make and model it was…
CR enables to update a line of text. Print some text without newline at the end, print CR, print another line, and the old text will be overwritten with the new one.
Some command-line apps are using the trick to print progress bars, progress percentage, or similar. This is very beneficial.
printf( "\rsomething " ) is much easier.
RFC3676, RFC2045, among others. CRLF is the Internet standard newline
Since you never know if it's a backslash or an actual Yen sign. Total fun.
The separate halfwidth yen sign does not exist in Shift-JIS at all. So any time a non-unicode Japanese Win32 application needs to draw a halfwidth yen sign, it must draw a backslash. And you can't make the official character mapping convert Backslash 0x5C into Yen sign 0xA5, otherwise all file paths would break.
It’s ok to hate Microsoft, but hate them based on facts, and not whatever nonsense you have in mind.
CRLF is actually the Internet standard. It wasn't something Microsoft came up with, it was something they inherited from DEC
> Yes, Windows owes it's origin to OS/2, and OS/2 used '\' instead of '/' - so we should never correct a decision inherited from an OS that died 22 years ago...
That's not what the article actually says though. DOS 2.0 decided on '\' instead of '/', and both OS/2 and Windows inherited that decision from DOS.
The major system that won't accept this is cmd.exe so if you are passing parameters to a .bat file that has a filename with a "/" in you will have trouble.
path = HOME / "cards" / f"{card_id}" / "card.yaml" path = f"{HOME}/cards/{card_id}/card.yaml"
Easier to read & write IMO, and works fine. Even on Windows. path = Path(“/foo”) / “a” / “b” / “c”
The following is functionally equivalent to the above: path = Path(“/foo”) / “a/b/c”
Which I really think was a mistake. The latter should resolve to “/foo/a\/b\/c” (IMO) because all other invalid characters (like space) are auto-escaped. What’s the point of the overloaded div operator otherwise?Even in modern versions of macOS, the GUI still allows you to create files and folders with slashes in their names; the slashes get recorded as colons in the filesystem.
> - The path is limited to MAX_PATH (260 chars)
> - Unless you prefix the path with \\?\
> - However using \\?\ disables automatic conversion of / to \ in the path (and some other normalization)
See previous discussion[1]
[0] https://mastodon.gamedev.place/@AshleyGullen/111109299141510...
Most of these are due to last minute changes to achieve a greater degree of compatibility with IBM's implementation of MS-DOS (PC DOS). This includes the use of "\" instead of "/" as the path separator, and "/" instead of "-" as the switch character.
https://github.com/microsoft/MS-DOS/blob/80ab2fddfdf30f09f0a...
all of it came from QDOS, a CP/M-like OS M$ bought based on hiding their plans, and that IBM made PC-DOS from. PC-DOS shipped with the original IBM PC.
MS-DOS only became a thing once there were "IBM PC compatible" clones. It was very smart of M$ to see that coming. They kind of rode IBM's coattails into dominance in the market. Back then, personal computers were oddities and when IBM entered the game, their reputation gave them credibility.
I don't think 86-DOS (aka QDOS) itself had slash-based options. At least, in a quick skim of the 86-DOS 0.3 user manual I couldn't see any. [0]
But they were there in PC-DOS 1.0, which suggests Microsoft added them after licensing 86-DOS.
[0] https://web.archive.org/web/20160409042609/http://www.paters...
I personally do not use Windows but most of our errors are reported by Windows users where sometimes path parsing is a problem or the drivers mess up vulkan configuration.
The fact that node takes a different approach to windows paths and sometimes the need to store these paths in storage and re-use them in external spawned processes makes it difficult to work with Windows' different and not so ideal path standards.
I’d turn that around. Sounds like Upscayl has caused a lot of issues for Windows users. Not developing on and doing little testing on Windows sure sounds like the problem to me.
> Not developing on and doing little testing on Windows sure sounds like the problem to me.
Most open source developers are not going to switch to "developing on" Windows because of posts like this. Nor should they. Upscayl is on GitHub and would probably appreciate some testing and fixing on Windows.
As a developer, I don't use Windows at all. When I make cross platform software, dealing with MS-DOS anachronisms isn't interesting or fun. For paid work, yeah, OK I'll do it. But for free stuff, I can see where a developer might not want to put a lot of effort there and focus on the interesting part of their project.
That's perfectly fine so long as one doesn't claim to be cross-platform (or supports Windows).
There might be Windows specific bugs but those are fixed as soon as the community gives feedback.
Being a cross platform application, Windows gets the same stack but is the cause of more unnecessary issues.
It is true that we don't have enough testing for Windows but that's because I do not have a Windows machine and Upscayl won't work in a VM so I have to rely on community feedback most of the time.
Originally it was on Windows only and I personally did the work required to make it work cross platform once .NET Core became mature enough, so I’m very familiar with the path separator challenges.
It was a bit of a pain, but not a huge deal. The trick was to just use forward slashes all the time, except for display or storing an absolute path.
Windows doesn’t seem to care when you use forward slashes for everything even if looks wrong.
Admittedly the Node code dealing with paths is limited as most of the heavy work in our app is done in a .NET background agent, but there are things like file selection dialogues and config loading that is in the Node part.
Based on the fact I can’t remember exactly what I did for paths in Node leads me to believe it was pretty much a non-issue, I probably used forward slashes and it just worked.
It took a lot more work for the .NET aspect where we’re doing a lot more stuff on the file system, I had to find and replace most path usages with a utility function to convert the separators appropriately.
Works great on my Mac.
Give these people a Nordic keyboard (it’s Alt+Gr plus one of the keys that is hardest to press while holding Alt+Gr).
It's included in most Linuxes and KDE and the homepage has a download for Windows and Mac as well as a nice keymap: https://eurkey.steffen.bruentjen.eu/
surely not - i used cp/m 1.x back in the late 1970s and by that time VMS was a well-developed and sophisticated OS, unlike cp/m which was something you could have knocked up in a week or so.
Not really, no.
It's not that they weren't used. It's that they didn't exist yet.
The first 8-bit microprocessor, the Intel 8008, was 1972. The first chip for which CP/M was developed was the 8080 in 1974. It took a few years for mass availability.
Given that you got your years badly wrong in your first comment, did you not think "hey I should check this" before commenting again?
FORMAT C: /S /U
The option-indicator in UNIX-like systems was the dash/hyphen '-' ls -al DIR C:/p/s/w
Doesn't do what you might expect.It's really really hard to imagine the usage for slashes as arguments was not a carry over. And for anyone old enough to actually remember the transition from CP/M to MS-DOS (like me), it felt very natural.
It wasn't a carry-over from CP/M itself. The utilities shipped with CP/M itself didn't use slash as an option character. PIP put options in square brackets.
It was a carry-over from Microsoft's programming tools for CP/M. And, as the article points out, Microsoft appears to have borrowed that syntax from DEC TOPS-10, where it originated
> a carry-over from CP/M
No, it wasn't, and I posted this correcting someone else on HN sharing this disinformation.
CP/M does not have command switches as standard. There is no standard separator because it didn't support switches. (Some programs did on their own.)
Didn't you RTFA before posting this -- gods, I hate the term -- fake news?
Seriously, in Caddy, I have some of the most bizarre special code paths for Windows. For example: https://github.com/caddyserver/caddy/blob/c6eb186064091c79f4...
Can someone who understands and appreciates the way Windows does files please help me appreciate it too?
This article did not help me have gratitude for Windows: https://www.fileside.app/blog/2023-03-17_windows-file-paths/
> we realise that we can construct an almost unlimited number of different path strings that all refer to the same directory.
:o
Easily changed in the registry/ with group policy, though 256 should not be the default today.
For example the built in .zip file support can’t extract long paths (7-zip however has no issue).
Also my Git client of choice (TortoiseGit) doesn’t fully handle long paths.
But day to day long paths on Windows is fortunately not really an issue any more and they’ve been steadily improving support over the years.
Despite generally being a fan of Windows, before Windows 10 the long path situation was infuriating, they even had a blog post acknowledging it’s a problem, but also stating it was going to be very non trivial to fix and as such they didn’t have any particular plans to fix it.
I suspect that WSL made the limitation more regularly painful enough that they could convince managers that it’s something that had to get fixed.
Windows Terminal was probably also motivated by WSL.
NTFS has always supported 32k characters in the path with each part being no more than 255 characters.
Office, OTOH, does not respect/observe MAX_PATH. Do not exceed ~240 characters in the path + filename for an Office document as Office will be unable to open it.
- a path is not a unique identifier of a file system object. They don’t have canonical names.
- don’t make any assumptions about case sensitivity, what file names are valid, or what the longest possible path is.
- avoid baking paths from strings as as much as possible. Use constructs like PathBuf (Rust) that prevent footguns like duplicated or incorrect dir separators. Never use a literal path separator char ‘/‘ or ‘\’ in code is a good rule of thumb.
In my experience, a program written in modern .NET 5+ and tested on Windows has a high probability to run flawlessly on Linux, without even a recompilation.
I can rely on Windows to still be running nice and happy a year later with no attention paid.
I can rely on Linux to die to a stiff breeze.
(Note: I'm probably cursed.)
It is the direct opposite of 36 years of extensive professional experience.
Linux is pretty tough. It will run on anything, and can cope with a lot of changes and damage.
21st century Windows is the most fragile OS I've worked with since classic MacOS.
* It seems you decide whether to execute a file by its extension, e.g. `.php`
* You are complaining that Windows is stripping away spaces, so `.php ` becomes `.php`
* And supposedly this could lead the file being served as static text if you didn't have a Windows workaround?
It rather seems to me that POSIX accepting `.php ` as a filename, and this not being picked up by a `.php` check is problematic here.
If that test fails we could serve PHP source code instead of having it be evaluated, a potential security flaw.
I guess Go hooks into too high level of an API. But you can prefix all your paths with "\\?\" to "force it" to be evaluated as is.
Consider the following program:
package main
import "os"
func main() {
os.Mkdir(os.Args[1], 0777)
}
Invoking test.exe c:\test... will create c:\test
Invoking test.exe \\?\c:\test... will create c:\test...See: https://learn.microsoft.com/en-ca/windows/win32/fileio/namin...
The link also explains why the file path length limit and details how to disable the limit.
LPSTR path = "C:\\Program Files";
They really should fix it. Better late than never.I really like the plan 9 ethos here, which can be summed up as "does it vaguely look like a tree? if yes, put it in the file system". My favorite one was html "Hmmm... An html document is tree structured.... hey guys lets make our web browser a filesystem driver. (enthusiastic clapping)". Having said that, having no mainstream browser on plan9 is what usually keeps me from using it. I keep telling myself that avoiding the web drivel that requires a mainstream browser would make me a happier person, but I keep coming back for more.
Well, me too, but it is 2024 and we have half a dozen technologies that let us put a few terabytes of non-volatile directly-accessible memory on the CPU memory bus.
Isn't it maybe time we relegated the technology of sequentially-accessed disk storage -- including SSD -- to legacy status, like tape drives, and stopped thinking about files and folders at all?
We are one quarter of the way through the 21st century. "Disk drives", including flash memory pretending to be disks, is legacy tech. We do not need filesystems any more. Let's move on and banish this to VMs holding legacy OSes for backwards compatibility with existing workloads.
Now, theoretically, all memory access on the system could be done via one simple api. and as plan9 showed most IO can be done via that same api. It is sort of like having all networks be IP or all protocols be HTTP. The implementation may not be ideal but having that narrow waist is a huge quality of life improvement.
Realistically, it is only used for items that need to persist outside of a processes run cycle.
As in a cartoon I once saw: two cats are talking, and one says of the human they're looking at: "it's a box of electronics, and when he presses buttons on the front, it flashes little coloured lights on that panel at him. It keeps him occupied for hours."
It's true and 100% accurate, but it's not at all precise.
It doesn't matter what kind of store it is. What matters is that it is an indirection mechanism designed for computers which were unable to access large amounts of directly-addressible non-volatile store.
Computers no longer suffer from this restriction, but all current OSes are built around this abstraction, and it cripples them.
somehow C: is easier to type than \\?\GLOBALROOT\Device\HarddiskVolume2\ or \\?\GLOBALROOT\Device\Harddisk0\Partition2\ or even \\?\Volume{1ab2d2f9-230f-4c63-bc25-163c085334df}\ though.
A lot of what gets blamed on Microsoft is really down to poor third-party software. Microsoft goes to extremes to keep that stuff running.
In the early DOS days, you didn't always have hard drives, but you'd always have floppy disks to boot from, and those letters were used first alphabetically.
Look, this is not a personal attack, but the reason I posted the link was that it answers the question... and your comment seems to indicate you didn't read it before commenting.
So why comment at all then?
And did you not think "hey, if I comment without reading and immediately indulge in speculation I will make it plain to everyone that I didn't read it and I am just guessing"?