88

When typing cd.. without a space between cd and .. the Windows command prompt will happily switch to the parent folder. Is there an explanation for this behaviour? The command does not follow the standard format of command<space>arguments

Working but shouldn't?

Also, why does this not create consistent results?

echo..

15
  • 24
    The premise of your question appears to be broken. Can you provide any evidence for your claim that this is syntactically incorrect? Jun 6, 2016 at 11:28
  • 14
    I don't think "command<space>arguments" has ever been the standard format in cmd (or any of its predecesors); consider for example dir/a or the similar VMS syntax. Jun 6, 2016 at 11:49
  • 6
    cd is very special. You can type cd c:\program files without quotes and it still works
    – phuclv
    Jun 6, 2016 at 13:09
  • 21
    Here is a very entertaining article which explains the quirks of the Windows shell logic, and what mayhem it can cause: thedailywtf.com/articles/The-Core-Launcher
    – vsz
    Jun 6, 2016 at 14:08
  • 3
    Why does cd.. work? Because Microsoft went through the trouble of explicitly making it work. cd is a command built into Windows' command interpreter, and Microsoft can make their interpreter do whatever they want. (As another example, cd also doesn't need quoting around directories with spaces in the name.)
    – jamesdlin
    Jun 8, 2016 at 1:59

5 Answers 5

109

As some of the other answers/comments note, the idea that there must be a space after the command is not correct. A well-known example is that you can type a forward slash after a command, without needing a space first.

However, there is another behavior which is a bit less understood, and allows the "cd.." that you are asking about. This behavior also allows for "cd\" to work.

The behavior that you describe is consistent for all commands internal to the command line interpreter. If you have certain symbols, including a period, forward slash, or backslash, then the prior characters are checked to see if they are a command that is internal to the "command line interpreter" shell (CMD.EXE or its predecessor COMMAND.COM).

This may be done before checking whether the word may refer to a file or subdirectory. That is true for the cd command. Tragically, when making a sample, I found that this does not happen with the copy command, so results are inconsistent: they are not necessarily the same with all internal commands. I didn't continue my search to also compare (much) other commands line del and dir, so I simply suggest being extremely careful if you try to rely on what happens without a space.

Now, the question also asked about the echo command: This is an unusual exception, in that I think echo. is fairly well-known by DOS experts. This probably was documented. The behavior, at least in Win7's CMD, is that if the command starts with "echo.", then the first period is ignored. So, "echo..hi" turns into output of ".hi". The reason for this is so that "echo." can be used to print a blank line. In contrast, with Unix, you can do this simply by running the "echo" command by itself. However, in DOS, running the "echo" command by itself will output the current "echo" setting. Similarly, DOS treats "Echo *Off*" and "Echo *On*" as special values that change the current echo setting. If you actually want to print the word "Off", then "Echo.Off" does the trick (at least with recent-enough versions of Microsoft's CMD command line interpreter.)

So, at least the echo command has a semi-reasonable explanation. As for the remaining commands, I used to think that internal commands had priority. However, when I tried to make some tests, I found that is actually rather inconsistent. I demonstrate this through some examples that I documented here.


Here are some examples. I did use an elevated command prompt, so that UAC wouldn't gripe about me writing to the root directory. This was done with Microsoft Windows 7's CMD.EXE. I suspect behaviors may be different with other versions, like COMMAND.COM from older MS-DOS versions, or software released by other companies (DR-DOS's COMMAND.COM).

(This answer is fairly long already, so I'm not including commands to clean up all of the mess I made on my filesystem. There's a tiny bit of clean-up, but not much.)

Here is an example that proves that the internal command creates precedence. (I also demonstrate the rather little-known ability to use a double colon to effectively be a comment, which also works well in batch files. Technically, in batch files, it gets processed as a label which cannot be reached by GOTO, and ends up being faster than the REM command.)

C:\something> md cd
C:\something> echo echo subdir >> cd\a.bat
C:\something> md \a
C:\something> .\cd\a.bat
subdir

C:\something> :: That ran from the subdir
C:\something> cd\a
C:\a> :: That changed my current directory, so cd took precedence

Update: Upon further experimentation, I found that the internal cd command only takes precedence over the filesystem if the specified directory does not include a period. So if you have a directory named "a.bat", then you can run "**cd\a.bat**" and the batch file will run.

This exploration of less common behavior (since most directories probably do not have periods in them) led me to update my findings. It turns out that the cd command is actually behaving more similar to the copy command than I initially thought.

Although I initially thought the cd and copy commands were behaving different, I now figured out that is because of the pattern of the names I was providing. Still, I reviewed my earlier results, and determined that my earlier documented tests do help to show some of the difference between what happens when a name includes a period and an extension, and when it doesn't. So, I'm still including my older findings below (mostly unchanged, but with some very minor updates so what I say is accurate).

Here is an example demonstrating copy, with a full path, does not use the same precedence (favoring the internal command) as cd when no extension is used:

C:\something> echo echo root >> \try.bat
C:\something> md copy
C:\something> echo echo subdirectory >> copy\try.bat
C:\something> .\copy\try.bat runs from the subdirectory
subdirectory

C:\something> copy\try.bat
subdirectory

C:\something> :: Huh? Why didn't that override and run from root?
C:\something> :: Apparently, the internal copy command didn't have priority over the check for a subdirectory and full filename (even though the internal cd command did take priority, when the directory had no extension)
C:\something> ::
C:\something> :: Another test: I can add useless periods at the end
C:\something> .\copy..\try.bat
subdirectory

C:\something> :: Okay, great. But then this will not check the subdirectory:
C:\something> copy..\try.bat
        1 file(s) copied.

C:\something> ::That ran the internal copy command

My early findings indicated that these results demonstrate that the command line shell gives priority:

  • to the filesystem (instead of the internal copy command) when specifying a backslash right after the name of the internal command
  • to the internal cd command (instead of the filesystem) when specifying a backslash right after the name of the internal command.
  • to the internal copy command (instead of the filesystem) when specifying a period right after the name of the internal command.

This clearly demonstrates that the behavior is not consistent between the copy command (with a full filename including an extension) and the cd command (without an extension as part of the directory name). When using a backslash, the copy command (with the full filename extension) will check the filesystem first, but the cd command won't (if the directory does not contain an extension).

(Update: At first, I thought the inconsistency was based on different behavior between the programs. Later, I discovered that the inconsistency did exist, but was caused more from the provided parameters.)

Actually, even those bullet points are not entirely accurate, even though I did just seem to demonstrate every individual thing I just said. The problem is, that list of bullet points are not precise enough to be completely accurate. (I left things imprecise so that those bullet points could be compared relatively easily, and checked relatively easily.)

However, to be more accurate, the first bullet point should point out that the command line shell gives priority:

  • to the filesystem (instead of the internal copy command) when specifying a backslash, and then the remainder of the full path, right after the name of the internal command

The following will demonstrate why I'm making that distinction:

C:\elsewhere> echo UAC elevation needed for this line >> \needext
C:\elsewhere> echo UAC elevation needed for this line >> \needext.bat
C:\elsewhere> md.\copy
C:\elsewhere> echo @Echo subdir >> copy\needext.bat
C:\elsewhere> .\copy\needext
subdir

C:\elsewhere> copy\needext.bat
subdir

C:\elsewhere> copy\needext
        1 file(s) copied.

C:\elsewhere> :: UAC also needed for the next lines
C:\elsewhere> del \needext
C:\elsewhere> del \needext.bat

(Note that the last copy command looked for the file called \needext, because the internal copy command was used. The file \needext.bat was only created to help easily show that it never got used by the command lines that included the word copy.)

At this point, I established some inconsistency (with the behavior of the copy command) when a backslash is used...

Next I will demonstrate that there is some consistency between these commands. (So, there is consistency... um... sometimes. We might only have consistency, inconsistently.) What I will show next is that the cd command does behave rather like the copy command when a period is used. The copy command uses the internal command, and so does the cd command.

C:\something> md.\yetmore

C:\something> cd.\yetmore

C:\something\yetmore> md.\md
C:\something\yetmore> echo echo subdir >> md\test.bat
C:\something\yetmore> .\md.\test
subdir

C:\something\yetmore> md.\test

C:\something\yetmore> md.\test
A subdirectory or file .\test already exists.

C:\something\yetmore> :: That error shows we ran the internal command.
C:\something\yetmore> md..\test
C:\something\yetmore> md.\cd
C:\something\yetmore> copy.\md cd
.\md\test.bat
        1 file(s) copied.

C:\something\yetmore> .\cd.\test
subdir

C:\something\yetmore> cd.\test
C:\something\yetmore\test> ::the internal command worked with one period
C:\something\yetmore\test> cd..
C:\something\yetmore> .\cd..\test
subdir

C:\something\yetmore> cd..\test
C:\something\test> :: internal command also took priority when two periods are used

So, during the initial test session which mostly focused on the cd and copy commands (with some additional usage of md and a bit of del), the only time that we really had the filesystem take priority was with the copy command, and then the filesystem was only taking priority when using the full path.

After subsequent review, I found that the cd command also gave the filesystem priority when using an extension. At least this means the internal commands are being treated a bit more consistent with each other. However, it also means that we get different behavior based on the names of the filesystem objects (the files or directories). That seems like the behavior is using some really, really obscure internal logic. Therefore, counting on this behavior to work across different operating systems is something I would probably consider to be unsafe to do.

6
  • "The behavior that you describe is consistent for all commands internal to the command line interpreter." ipconfig for example works too. Jun 6, 2016 at 19:48
  • @JonasKöritz : No. You can put a (forward) slash right after the command "IPConfig" and that will work, e.g. IPCONFIG/ALL. However, that's not what I was talking about. "The behavior that you describe" (in your question) was the behavior of placing a period right after the command name. If I type IPConfig. then I get an error about a command not being found. Similarly (although this is not related to the behavior you were describing), if I type IPCONFIG\ALL then I can run a custom .\IPCONFIG\ALL.BAT file that I made. So /'s are not treated like . or `\`
    – TOOGAM
    Jun 6, 2016 at 19:55
  • 1
    I will accept your answer to appreciate the work and research it took to create it! Jun 6, 2016 at 19:58
  • 2
    @Calchas Simple test - try executing copy.exe or copy.com in cmd. It doesn't work - it's not an executable.
    – Luaan
    Jun 8, 2016 at 13:03
  • 1
    @Luann : Regarding ipconfig, I disagree with your conclusion. This question is about what's typeed at the start of a command line. Windows/DOS identifies executables by filename extension, so you can't run a program called "ipconfig" without an extension (in Windows, unlike Unix which permits this). Regarding the next comment, I don't know who "Calchas" is. (When you specify an at sign, following is usually the first characters of a user that shows up elsewhere on the page.) I agree, running "copy.exe" will use the internal copy command (and pass .exe). (You can run .\copy.exe)
    – TOOGAM
    Jun 8, 2016 at 15:02
42

You assume that a command name and its arguments must be separated by a space, specifically, but this is not true. As long as the invocation can be unambiguously interpreted, the invocation is valid.

In this case, the first argument begins with . and . cannot be part of the command name, so cd and .. are simply parsed as two separate tokens.

Usually, your first argument will start with an alphabetic character (e.g. the beginning of a path), so it'll "bleed" into your command name and cause an error… but that is not a syntax problem. It's a semantic one.

You can see the same effect at work with other commands, including echo:

echo...
..

In this case, we get only two periods because the echo command itself has a special rule, so that the following:

echo .

or, by extension, this:

echo.

outputs only an empty line. It's a convenience. Apparently it's been implemented by ignoring a leading period in the argument.

Hey, this is DOS/Batch. You want sanity? :D

16
  • 2
    I assumed this because it is unique to windows command line, bash for example will not allow you do do cd.. Jun 6, 2016 at 11:41
  • 47
    @JonasKöritz: That's a completely different program on an entirely different operating system. My bicycle doesn't allow cd.. either :) Jun 6, 2016 at 11:43
  • 16
    @JonasKöritz: alias cd..='cd ..'
    – mouviciel
    Jun 6, 2016 at 12:10
  • 5
    @LightnessRacesinOrbit: Originally it was a shortcut of filtering out . and .. which appear as directories in every other directory and as far as I know was never intended to catch more than that. I actually consider hidden-ness modelled as file attribute a cleaner design than having it implicitly follow from the file name.
    – Joey
    Jun 6, 2016 at 14:20
  • 4
    @joey - I think the more relevant point is not that the DOS approach was simpler, it's that DOS didn't allow . in filenames, which meant it couldn't be a part of a command name therefore must have been part of the argument. Even if DOS had divided the command into arguments as Unix shells do, it would still place a . after the command into the first argument, because it would make no sense to put an invalid character into the command name.
    – Jules
    Jun 6, 2016 at 15:00
18

The cd..command is correct and it was defined like that in the original command interpreter command.com which later was named cmd.exe.

The command interpreter knows how to process cd.., because . is a special character, just like \.

11
  • 8
    I guess the main issue is that the command cannot be "syntactically incorrect" because the syntax is not formally specified anywhere, so if the primary implementation (cmd.exe and/or MS-DOS) accepts it, then it has to be correct. Jun 6, 2016 at 10:47
  • 3
    Also, CD is not a program, but an internal command. Similar to Echo which also is an internal command, it does not need to have a space for it to work. echo. works just as well, which will print an empty line.
    – LPChip
    Jun 6, 2016 at 10:52
  • 2
    @Overmind echoe will not work either, so its the same with cd, echo, md, etc.
    – LPChip
    Jun 6, 2016 at 10:59
  • 2
    @JonasKöritz , the . is not dropped but just a space is added. md.test and md .test both create the directory .test. Typing cd.test and cd .test will change into the directory .test. Jun 6, 2016 at 11:35
  • 7
    @JonasKöritz: Because the syntax has never been command-space-arguments. Jun 6, 2016 at 11:38
12

It's a backwards compatibility hack.

The command line interpreter is designed to be backwards compatible with commands from the original MSDOS command interpreter, which was designed to be backwards compatible with the CP/M command interpreter. Neither CP/M nor MSDOS allowed a . in a filename (it was interpreted as a separator between two parts of the filename, the base name and the extension). This meant that (at least for early versions of DOS), the command interpreter could identify that if it reached a '.' (or indeed any other character that was illegal in a filename) it passed the end of the command name and was into the command arguments. This was quite commonly used in both DOS and CP/M -- for example, dir/w was a very common command, equivalent to dir /w meaning to list files in a horizontal format.

Nowadays, '.' can appear in filenames. This causes some complications in how to parse commands, but the shell still identifies a . that is not part of an exact filename as being the beginning of arguments. This is necessary largely because millions of users have grown used to typing cd.. or have large numbers of batch files containing that or echo. or any number of other similar commands.

0

Modern Powershell defines the syntax more clearly, but cd.. still works.

However I notice it is just a script:

> get-command "cd.." | select *

ScriptBlock         : Set-Location ..
CommandType         : Function
. . .

(cd itself is an alias for Set-Location).

Meanwhile cd. (etc.) fails.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .