Why does "cd.." work in the Windows command line?

17,384

Solution 1

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.

Solution 2

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

Solution 3

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 \.

Solution 4

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.

Share:
17,384

Related videos on Youtube

Jonas Köritz
Author by

Jonas Köritz

BY DAY: Cyber Security Consultant AT NIGHT: Software Developer FOR FUN: Reverse Engineering

Updated on September 18, 2022

Comments

  • Jonas Köritz
    Jonas Köritz almost 2 years

    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..

    • Admin
      Admin about 8 years
      The premise of your question appears to be broken. Can you provide any evidence for your claim that this is syntactically incorrect?
    • Admin
      Admin about 8 years
      Added another example that raises questions for me...
    • Admin
      Admin about 8 years
      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.
    • Admin
      Admin about 8 years
      @JonasKöritz: Updated my answer.
    • Admin
      Admin about 8 years
      cd is very special. You can type cd c:\program files without quotes and it still works
    • Admin
      Admin about 8 years
      also try echo), echo(, echo[... and see
    • Admin
      Admin about 8 years
      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
    • Admin
      Admin about 8 years
      @LưuVĩnhPhúc : +1 to your comment as these are my findings: echo(, seems to operate similar to echo., as does using [, ], =, +, ;, :, \\ (and, most surprisingly, echo/) by eliminating the first character after the word "echo". Trying ), {, }, ~, _, or -** (or "back quote/tick") do not have the special effect, and expectedly complain that the command is not recognized as a valid command. echo^ chops the first character, but echo^^ and echo^^^ error identically (showing only one ^ in output), while echo^^^^ doesn't match.
    • Admin
      Admin about 8 years
      @vsz : The article was rather interesting. Although, it wasn't about the command line, but rather was about a CreateProcess() function, which is used by the Start\Run dialog box, according to comments (which referenced MSDN documentation). So, slightly different topic.
    • Admin
      Admin about 8 years
      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.)
    • Admin
      Admin about 8 years
      I'm pretty sure Raymond Chen (The Old New Thing) wrote about this. Ofcourse I can't find it now. And that makes this comment pretty useless; except that maybe someone else's google-fu is better than mine and can dig up the post in question.
    • Admin
      Admin about 8 years
      Tangent: I remember an old utility program called doskey which, when installed, added variations on DOS command prompts. The main one I miss is adding dots to the cd.. (or cd ..) command to substitute for higher directores. So, cd... was the equivalent of cd ..\.. (or cd..\..), cd.... was the equivalent of cd ..\..\.. etc. I haven't had doskey in a donkey's age, but I still sometimes try to type those commands.
    • Admin
      Admin about 8 years
      @vsz That's horrifying. TIL the Windows command interpreter isn't even deterministic.
    • Admin
      Admin about 8 years
      @vsz, that's a very good read, thanks. I didn't know Windows actually had any logic, especially its shell. :)
    • Admin
      Admin about 8 years
      There is discussion at various points on this page that there isn't a defined syntax for these commands. In fact, there was. IBM's Command Reference for OS/2 had the syntax for all of cmd's commands given in the form of railroad diagrams. No, the IBM-specified syntax did not allow for non-spaces in these positions. I have written on this subject. So has Rex Conn.
  • user1686
    user1686 about 8 years
    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.
  • LPChip
    LPChip about 8 years
    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.
  • Overmind
    Overmind about 8 years
    Well good old DOS times when people were digging into every OS file certainly helps with this sort of things. Very few thing of the good old days no longer work in regard to command line. Command.com was created to be able to read special characters after any given command. Just as a test, try "MD." and "MD..". Don't worry, it won't break anything. @LPChip - not like echo, since something like 'cdc' will not work, but "cd." will be interpreted.
  • Jonas Köritz
    Jonas Köritz about 8 years
    Why didn't they stick with command-space-arguments syntax?
  • LPChip
    LPChip about 8 years
    @JonasKöritz DOS was one of the first OSses. They probably wanted to type cd... as quicker than cd .. Why they did it is something we can only guess.
  • LPChip
    LPChip about 8 years
    @Overmind echoe will not work either, so its the same with cd, echo, md, etc.
  • Overmind
    Overmind about 8 years
    Yes, LDChip, but echo.e will work, since '.' is special character. So the rule can be defined as 'no need for space if theres a special character involved'.
  • Jonas Köritz
    Jonas Köritz about 8 years
    @Overmind, wouldn't this result in cd . if any special character is ignored?
  • daniel.heydebreck
    daniel.heydebreck about 8 years
    @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.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @JonasKöritz: Because the syntax has never been command-space-arguments.
  • Jonas Köritz
    Jonas Köritz about 8 years
    I assumed this because it is unique to windows command line, bash for example will not allow you do do cd..
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @JonasKöritz: That's a completely different program on an entirely different operating system. My bicycle doesn't allow cd.. either :)
  • David Balažic
    David Balažic about 8 years
    As said the space is not mandatory, so dir.. and others already mentioned also work.
  • mouviciel
    mouviciel about 8 years
    @JonasKöritz: alias cd..='cd ..'
  • Joey
    Joey about 8 years
    @Jonas: The reason here is also that command.com/cmd don't really have a generic command/argument parser as it wasn't necessary. Unlike on Unix, processes on Windows get just a string of arguments, not an array. Therefore, the command processor doesn't need to do any parsing beyond the process name (i.e. up to the first unquoted space). The built-in commands (cd, etc.) are even older and I'm not even sure the parser wasn't written in Assembler back then – the whole parsing of built-in commands is inconsistent and full of surprises, mostly because it's grown organically instead of specified.
  • Luaan
    Luaan about 8 years
    @Joey Yup, exactly - the system evolved. When a bug started to be used by users, Microsoft had no choice but to support it in future versions. To be fair, the same thing is true of Unix - there's plenty of idiosyncracies that don't make any sense beyond the historical (e.g. "file names starting with . are invisible"). Unix was standardized long after most of those were already in place, and the standardization didn't really help much anyway - the differences between different POSIX-y systems are still huge, and they're quite incompatible unless you're very careful.
  • Luaan
    Luaan about 8 years
    @LPChip First versions of DOS didn't even have cd - what's the point of a "change directory" command when you don't have directories? :) But most importantly, there was no ambiguity - you couldn't execute a program named cd., since it wasn't an executable, and cd.. wouldn't be a valid file name at all. Of course, those are perfectly both valid executable filenames now, so the modern cmd has to be a lot smarter to make sure things work as expected.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @Luaan: What doesn't make sense about the leading-dot convention? It's extremely convenient.
  • Joey
    Joey about 8 years
    @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.
  • Matthew Ayers
    Matthew Ayers about 8 years
    @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.
  • Cand3r
    Cand3r about 8 years
    to add to this, ipconfig/all works just like ipconfig /all, but arp-a does not work while arp/? does. Is my wording right if I say 'there's a difference between parameters and arguments' ?
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @Cand3r: You'd be better off identifying that - counts as a character that may be part of a command name, thus it doesn't inherently make parsing proceed to the argument/parameter stage, unlike /. (Like if I live in "Paris,France" you know the comma is a delimiter between city and country; if I live in "Ashby-de-la-Zouch" that's just one word .. just the city. Even though neither example has even a single space character.) I'm not aware that there is a different name for /abc than for -abc, though.
  • Jonas Köritz
    Jonas Köritz about 8 years
    "The behavior that you describe is consistent for all commands internal to the command line interpreter." ipconfig for example works too.
  • TOOGAM
    TOOGAM about 8 years
    @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 `\`
  • Jonas Köritz
    Jonas Köritz about 8 years
    I will accept your answer to appreciate the work and research it took to create it!
  • nobody
    nobody about 8 years
    Why do you say ". cannot be part of the command name"? Try this: md .bat, echo echo batch > cd.bat, cd.bat. You'll see "batch" echoed - you won't end up in the .bat folder.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @AndrewMedico: Hmm, granted! It must do some backtracking then.
  • Luaan
    Luaan about 8 years
    The ipconfig example makes perfect sense, though. You'd use ipconfig. exactly when you wanted to run ipconfig. instead of e.g. ipconfig.exe -that is, you specify that you're referencing a file with no extension, rather than a file with a "default" extension. So while it may appear inconsistent, it's an inconsistency in a case where the "consistent" behaviour would introduce ambiguities. It's not really different from notepad myFile (which creates/opens myFile.txt) and notepad myFile. (which creates/opens myFile.).
  • Luaan
    Luaan about 8 years
    @Calchas Simple test - try executing copy.exe or copy.com in cmd. It doesn't work - it's not an executable.
  • Mardoxx
    Mardoxx about 8 years
    +1 for We might only have consistency, inconsistently.
  • TOOGAM
    TOOGAM about 8 years
    @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)
  • Kaz
    Kaz about 8 years
    To use words like "parse" and "token" in describing this cruft is very charitable.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 8 years
    @Kaz: lol indeed. I feel dirty trying to describe this stuff in any sort of rigourous way. But, needs must...