I couldn't find anything but is there any reason for choosing % over $ like in *nix shells?

  • 5
    Not only MS-DOS uses % instead of $, but the syntax is different. In *nix, the dollar sign is a sigil, and the percent sign in MS-DOS is more like a magic quotation mark, as variable names must be enclosed in percent signs to be interpolated, rather than just preceded by a percent sign (e. g. %PATH%). This avoids the need to have another magic character, like parentheses to indicate the variable name boundary, like in $(foo)bar. Then it makes sense to use a new character to indicate its new semantics.
    – Leo B.
    Aug 14, 2018 at 2:07
  • 7
    @LeoB. Nitpick: In Bash, $(foo) means "run command foo and insert its stdoutput into the command whereas (in POSIX shell also) ${foo} means "value of variable $foo.
    – wizzwizz4
    Aug 14, 2018 at 18:48
  • @wizzwizz4 I was thinking about the Makefile syntax where the parentheses are mandatory.
    – Leo B.
    Aug 14, 2018 at 19:16
  • 2
    ICL George III also used % but in a completely different way. There were 26 variables and you could do indirection using multiple % signs. The GEORGE equivalent of C's **A would be %%%%%A.
    – cup
    Sep 2, 2020 at 11:08
  • 2
    One might ask why Unix shells chose "$" when there was already an example of "%" being used for substitution-type operations, in C printf formats.
    – dave
    Sep 2, 2020 at 22:15

3 Answers 3


Because it was already reserved for batch file command-line parameters.

Early DOS versions (1.x) did not support environment variables at all. They did, however, support batch files and parameter substitution using the %n syntax (source). The character % was already reserved for that purpose, and already had to be escaped as %% in batch files; it made sense to re-use it for variable substitution, as the two features are quite similar.

The $ character, on the other hand, was already in use in names of temporary files, for example in EDLIN (source; the public source code is from the 2.0 version of DOS, but the binary in the v1.25/bin/ directory contains equivalent code, offset +0xd0 in the binary); subsequent DOS releases added more such uses. If the $ character were used for variables and the user wanted to delete a leftover temporary file with such a name (or, especially, if they wanted to have a batch file do it), the $ character would have to be escaped, which would be inconvenient (not to mention it would pose a slight backwards incompatibility). I am under the impression that Microsoft wanted to avoid adding more escape sequences to COMMAND.COM syntax, from the fact that for example they outright banished <, > and | from file names when pipes were introduced.

Of course, that in turn raises the question why % was used for batch file parameters instead of $, like in the equivalent CP/M functionality provided by the SUBMIT command. Batch files are supported in MS-/PC DOS 1.x, but apparently not in 86-DOS 0.3; the manual makes no mention of it. So it appears the functionality was added sometime between those two versions; this is confirmed by the 86-DOS license agreement between Microsoft and Seattle Computer Products, which (on page 8) mentions “SUBMIT facility comparable to CP/M” as one of the improvements to 86-DOS that Microsoft requested from SCP. We might guess that EDLIN was already using $ for temporary files before batch files were introduced, and indeed, if we disassemble the version of EDLIN found on the even earlier 86-DOS 0.1 floppy image, we find this:

        ; RBIL #01378, 5Ch:
        ; first default FCB, filled in from first commandline argument
        mov         si, 0x5c       ; +0x002d  be5c00
        mov         di, 0x5fb      ; +0x0030  bffb05
        mov         cx, 9          ; +0x0033  b90900
        rep movsb                  ; +0x0036  f3a4
        mov         si, 0x528      ; +0x0038  be2805
        movsw                      ; +0x003b  a5
        movsb                      ; +0x003c  a4

        ; INT 21 - DOS 1+ - DELETE FILE USING FCB
        mov         ah, 0x13       ; +0x003d  b413
        mov         dx, 0x5fb      ; +0x003f  bafb05
        int         0x21           ; +0x0042  cd21

        mov         al, '$'        ; +0x0044  b024
        mov         di, 0x5fb+9    ; +0x0046  bf0406
        stosb                      ; +0x0049  aa
        stosb                      ; +0x004a  aa
        stosb                      ; +0x004b  aa

        ; INT 21 - DOS 1+ - DELETE FILE USING FCB
        mov         ah, 0x13       ; +0x004c  b413
        int         0x21           ; +0x004e  cd21

        mov         ah, 0x16       ; +0x0050  b416
        int         0x21           ; +0x0052  cd21

So presumably % was chosen for that reason. But to be sure you’d probably have to ask Tim Paterson; otherwise your guess is as good as mine.

  • Answer can be improved: at some point ^ was added to escape. When was that?
    – Joshua
    Dec 12, 2021 at 22:29
  • 1
    No earlier than in Windows NT, maybe OS/2. No version of MS-DOS supported it. Dec 12, 2021 at 22:41
  • 3
    In many DEC systems, '$' was used as a reserved-to-DEC character in symbol names, as a primitive way to separate the namespace into DEC-use and customer-use. The 'softies would be familiar with this, and I think that's how '$' is used in DOS etc.
    – dave
    Dec 17, 2022 at 12:27

By using a \ as path separator, they needed a different marker for control character encoding, and did choose $ which in turn meant that the variable marker need to be another one, which turned out as %.

So by choosing % instead they could have kept $ for variables, but then again is the parser structured a bit different as it allows concatenated strings, thus needing a finishing marker at the end of named variables. So they didn't and the rest is history :))

I'm not complete so sure right now about the full implications, but $ is used at least in three different ways on the MS-DOS command line and in batch.

  • Expand a batch variable to a full path name with %~$PATH:<varnumber>
  • Defining special content for the PROMPT command like date and time as in prompt $d $t$g.
  • Code the escape character in parameters as $e like echo $e[0m for screen reset.

Especially the does give the lead here, as marking special characters in Unix is done via a backslash \, which in DOS marks a directory level. MS had to use $ instead, so \e became $e.

Further, but way later (DOS 5)

  • DOSKEY used $ to indicate special functions in macro generation.

Surprisingly, its predecessor CP/M did use $ as variable indicator as well as marker for special meanings in some commands (like privileges in STAT).

In CP/M batch files are run with the SUBMIT command, which will read a file (standard ending .SUB) and feed each line to the OS, after replacing variables marked with $. DOS has this feature build in and treats .BAT files like programs.

  • 2
    In bash they resolve the ambiguities by adding brackets ${var}
    – phuclv
    Aug 14, 2018 at 0:55
  • 1
    CP/M's CCP doesn't give $ any special meaning. STAT and SUBMIT are transients (separate programs) that interpret command-line parameters or strings in the input themselves.
    – Blrfl
    Aug 14, 2018 at 13:28
  • 4
    @phuclv Remember that bash is a Johnny-come-lately; that strategy is of a far more ancient lineage.
    – tchrist
    Aug 14, 2018 at 13:28
  • 2
    The %~$PATH:<varnumber> syntax is only supported by cmd.exe, it was never available in DOS. And the bit about ‘control character encoding’ makes no sense. COMMAND.COM has no escape syntax for command lines (other than %% for the % character, and the syntax of the PROMPT variable). Sep 2, 2020 at 8:58
  • 1
    So the use of - changed the use of /, which changed the use of \ , which changed the use of $, which changed the use of %. There's a hole in my bucket, dear Liza, a hole.
    – DrSheldon
    Sep 2, 2020 at 18:49

I can't know for sure but I suspect one of the reasons was that $ was used as the string terminating character in the standard DOS "print string" API (int 21h ah=9h), a convention apparently inherited from CP/M.


You must log in to answer this question.

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