Quotation marks around spaces aren’t necessary in the PATH environment variable

Date:September 29, 2006 / year-entry #333
Tags:tipssupport
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20060929-06/?p=29533
Comments:    28
Summary:The purpose of quotation marks is to allow a character that would normally be interpreted as a delimiter to be included as part of a file name. Most of the time, this delimiter is the space. The CreateProcess function uses a space to separate the program name from its arguments. Most programs separate their command...

The purpose of quotation marks is to allow a character that would normally be interpreted as a delimiter to be included as part of a file name. Most of the time, this delimiter is the space. The CreateProcess function uses a space to separate the program name from its arguments. Most programs separate their command line arguments with a space. But the PATH environment variable doesn't use spaces to separate directories. It uses semicolons.

This means that if you want to add a directory with spaces in its name to the path, you don't need quotation marks since spaces mean nothing to the PATH environment variable. The quotation marks don't hurt, mind you, but they don't help either.

On the other hand, if the directory you want to add contains a semicolon in its name, then you do need the quotation marks, because it's the semicolon that you need to protect.


Comments (28)
  1. MSDN Archive says:

    Actually, sometimes the quotes do hurt. I’ve run into a couple of programs recently (can’t remember which now) that refused to load until I took the quotation marks out of the path variable. That took a while to figure out…

  2. ShyGuy says:

    They also hurt because they are characters in what is sometimes a very limited resource (on Win9x at least).

    There is a limit on length of env vars in Win9x (at least)

  3. Brian Hoyt says:

    I must admit I have never nor had thought of using ; in a dir.  I for some reason thought that was a forbidden character.

  4. And says:

    Why wasnt |(pipe) or * or some other invalid char used as the separator?

    [I trust you’re smart enough to answer this for yourself. -Raymond]
  5. Ma says:

    Quotation marks around spaces aren’t necessary in the PATH environment variable

    In Windows 9x, you NEED to use quotation marks for paths with spaces, at least in autoexec.bat

  6. Vipin says:

    In the past project, I had to place set of strings in a config file. The way I went about placing it was like this:-

    Strings=<len1>=<string1><len2>=<string2>….

    so the basic unit over there was <len>=<string>

    let us say the two strings were "hello" and "world" then in the config file, I place it like this:-

    Strings=5=Hello5=world

  7. igor_levicki says:

    And what happens if a program unaware of long paths parses PATH variable which contains spaces?

  8. Ben Cooke says:

    Igor,

    If I recall correctly, Windows fakes up some environment variables for 16-bit processes, including the PATH. It contains the short versions of the filenames from the perspective of such programs.

  9. hswear3 says:

    I always thought the semicolon was an invalid character for files and directories; but it is not. I just did an experiment on Windows XP.

    From cmd.exe, mkdir test1;test2 creates two directories but mkdir "test1;test2" creates one directory.

    Explorer does not complain at all and creates a folder named test1;test.

  10. jamiej says:

    Recently I came across a directory ending in a space. Explorer had no problem peering inside, however the command prompt could not even with quotes. This piqued my interest so I decided to see how .Net would handle it.

    System::IO::GetDirectories on the parent enumerated the folder. Its name included the trailing space. Using GetFiles on the folder threw DirectoryNotFoundException. I then tried to encapsulate the space using quotes and finally appending a slash to no avail.

  11. hobbes78 says:

    jamiej: that’s when dir’s /x switch comes in handy…

  12. RC5 says:

    Well, we all can understand the implication of using | or > or <.

    But * ?

    SET P=*.*

    sets P to.. *.*. So no expansion is done and it could easily be used when the system was designed as a separator.

    [Okay, I guess people don’t know their history. Unix uses a colon to separate directories in the PATH environment variable. Colon is a valid path character in MS-DOS (drive letter identifier), so the next closest invalid file name character was chosen – the semicolon. -Raymond]
  13. Ivo says:

    mikedodd, I had exactly the same problem just last week. Turned out the DirectX SDK inserted its UtilitiesBinx86 folder at the start of my PATH and used quotes.

  14. Dennis says:

    Rational’s ClearMake and IAR’s C compiler threw fits until we quoted the PATH environment variable.  Windows itself doesn’t mind but other people’s code who uses it may not interpret it right.

  15. Stefan Kanthak says:

    Ben Cooke (& hobbes78 too):

    Windows NTx does (and sometimes even can) not place the dreaded short filenames in environment variables or the registry: NTFS knows a setting "NTFSDisable8dot3NameCreation"!

    I’d encourage EVERY developer to turn this setting on (as early as possible, i.e. per modification of the SETUPREG.HIV on the installation media) to catch errors of bad installers!

    JFTR: PowerPoint 2003 Viewer and Excel 2003 Viewer both have errors in their installation routines (they come as *.msi; it’t sad that there is no automatic check in the tools or even the msiexec.exe) and don’t place " around the paths they write to the registry.

    Trustworthy computing in Redmond?

  16. Jon Galloway says:

    Raymond, this reminds me of something kind of similar that I ran into recently. .NET console applications seem to have problems with arguments which end in “, and I can’t tell if it’s a .NET thing, a Windows thing, something in between, or boneheadedness on my part. Care to comment?

    http://weblogs.asp.net/jgalloway/archive/2006/09/13/Command-Line-Confusion.aspx

    [Command line parsing is the program’s responsibility. -Raymond]
  17. Jon Galloway says:

    Re: Command line parsing, I understand that it’s the program’s responsibility, but it appears that my program’s not being given the arguments correctly. See my post for more details, but here’s an example that shows what I’m talking about. In this case, the Environment.CommandLine property is correct, but args[] (which is populated by the .NET framework) is missing characters.

    C:Temp>CommandLineArgumentsTest.exe “\test\” “\test\”

    Environment.CommandLine: CommandLineArgumentsTest.exe “\test\” “\test\”

    args[0]: \test

    args[1]: \test”

    [You’ve shown it yourself. Your program was given the arguments correctly by the core OS. The issue is that your program asks the .NET Framework to do the parsing, and apparently you don’t like the way they do it. The core OS isn’t involved. Its job is to give you the raw command line; the rest is up to you. -Raymond]
  18. windows.net = os says:

    .net is an os component.

    It’s should not be the app developer’s duty to parse the command line.

  19. BryanK says:

    .net is an os component.

    Well no, actually, it isn’t.  It’s an optional addon, see where Windows Update / Microsoft Update put it.

    See also this blog’s tagline:

    not actually a .NET blog

    Now:

    It’s should not be the app developer’s duty to parse the command line.

    There I agree; that’s why I think the way that Unix-like OSes do it is better.  Your main() function gets handed an array of strings, rather than one long string; the shell has already done the parsing, and the kernel has kept the array-of-strings setup all the way through until it starts your program.  (Or in the case of Windows, either cmd.exe or explorer.exe has done the parsing.)

    Now I realize the reason this doesn’t happen is because cmd.exe used to be command.com, and it used to be that DOS only had 640K of RAM available, and command.com had to use as little of that as possible.  But that argument just means that each program had to include the same parsing code over and over again (and get it wrong in many cases!  apparently including .net for some odd command lines); it didn’t mean the parsing code could go away.  Well, unless the program didn’t care about its arguments; then it could go away.

    For some reason, it just seems like a bad tradeoff…

  20. Norman Diamond says:

    Tuesday, October 03, 2006 9:09 AM by BryanK

    > .net is an os component.

    >

    Well no, actually, it isn’t.  It’s an

    optional addon

    Prior to Vista that’s true, it’s not a critical integrated component like IE.  However, the quoted poster was correct for Vista.

    See also this blog’s tagline:

    > not actually a .NET blog

    Of course that’s true.  Now I’m wondering how some .NET stuff sneaked into this blog a few months ago.

  21. BryanK says:

    Prior to Vista that’s true

    And since there is no such thing as Vista yet, only betas (and RCs?), it is still true.  And depending on how many times Vista gets pushed back again before it finally gets released, it may be true for quite some time yet.

    (OK, yes, I’m being pedantic now.  You’re right, I wasn’t paying any attention to Vista when I made that comment.)

    Regarding the "not actually a .NET blog" comment, my point was that since the issue is with .Net’s command line parsing code, and Raymond is on the shell team, he (1) probably has no way of even looking into the problem, and (2) very likely can’t do anything about it.

  22. Jon Galloway says:

    Carlos commented on my blog post and set me straight. It turns out that it’s not just a .NET thing, it’s a CommandLineToArgvW thing.

    "Most apps (including .Net apps) use CommandLineToArgvW to decode their command lines.  It uses crazy escaping rules which explain the behaviour you’re seeing."

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/commandlinetoargv.asp

    CommandLineToArgvW has a special interpretation of backslash characters when they are followed by a quotation mark character ("), as follows:

       * 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.

       * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.

       * n backslashes not followed by a quotation mark simply produce n backslashes.

  23. I recently posted on my confusion when I tried to use commandline arguments ending in " and got unpredictable

  24. rsclient says:

    A quick note about CommandLineToArgvW — these easiest way to get the command-line back in properly parsed form is—-to use the __argc and __argv global variables that *all* C (and C++) program are allowed to access.

    They aren’t documented very much, but they work like a charm.  And yes, I already reported to Microsoft that they should be mentioned more in the documentation :-)

    [It depends on your definition of “all”. Those internal variables are specific to the Microsoft Visual C runtime. If you use some other company’s C/C++ compiler you may not have those variables. Plus of course if they’re not documented then they might stop working at any time. But you knew that, right? I mean, you wouldn’t recommend people do stuff that might break in the future, woudl you? -Raymond]
  25. windows.net = os says:

    And since there is no such thing as Vista yet, only betas (and RCs?), it is still true.

    windows.net (server2k3) has .net framework installed, correct? At least it is bundled with windows ce .net, including windows mobile.

    When .net framework is installed, it’s an os component, much like directx (wasn’t bundled with win95a/b – but could be installed).

  26. use the standard please says:

    > Those internal variables are specific to the Microsoft Visual C runtime.

    Because argc & argv are broken, you are thus suggesting it would be better to use unportable OS specific functions (CommandLineToArgvW/GetCommandLineW)?

    If you use some other company’s OS you may not have those functions. But you knew that, right? I mean, you wouldn’t recommend people do stuff that might break in the future, would you?

    [If you use some other company’s compiler you may not have those variables. I don’t see what’s so hard about declaring your own global variables and assigning to them the first thing in your main(). Now your code is portable to all compilers and doesn’t use undocumented features. I’m not sure what you mean about breaking in the future. CommandLineToArgvW and GetCommandLineW are documented and supported. -Raymond]
  27. BryanK says:

    > Now your code is portable to all compilers

    No, now your code is portable to all *Windows* compilers.  Unless Unix (i.e. “some other company’s OS”) has a CommandLineToArgvW call that acts the way Windows’ does, that I’ve missed?  ;-P

    Using the values that the C library startup routine passes to your main() function is *supposed* to work on any OS that has a C compiler.

    (Now, if the program’s C library uses CommandLineToArgvW to get these values, it still won’t be portable as far as the user is concerned.  They’ll pass “\” on the command line, and expect one backslash and no quotes.  “\” will be expected to give one backslash and one quote (and not terminate the command line).  Or at least, this is what happens under any Bourne shell, and it seems a *heck* of a lot more logical to me.  But, the code will at least *compile* on other OSes.)

    (To clarify: I suspect that “use the standard please” meant that you should use the arguments to main(), not the undocumented __argc and __argv variables provided by only the Microsoft C library.  But I don’t know what he meant for sure, so maybe I shouldn’t assume it.  And since you were talking about the undocumented variables, it seems that his argument doesn’t make much sense in the context it was presented.)

    [I think we’re talking about different things. I’m saying that using the __argc and __argv variables is not portable. If you want portable, then do this:

    int program_argc;
    int program_argv;
    int main(int argc, char **argv)
    {
     program_argc = argc;
     program_argv = argv;
     ...
    }
    

    CommandLineToArgvW was somebody else’s suggestion. -Raymond]

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index