22

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

user3840170
  • 23,072
  • 4
  • 91
  • 150
phuclv
  • 3,592
  • 1
  • 19
  • 30
  • 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 '18 at 02: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 '18 at 18:48
  • @wizzwizz4 I was thinking about the Makefile syntax where the parentheses are mandatory. – Leo B. Aug 14 '18 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 02 '20 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 02 '20 at 22:15
  • The question implies that '$' is a better choice because Unix had already used it. I don't see anything to support that. In general, if you're looking for a funny character to use for something, you just pick what seems reasonable from what is so far unused and therefore available. – dave Sep 04 '20 at 11:33
  • @another-dave In fact, I think $ is a poor choice, especially as $<number> has a special meaning in Unix shells. What if I just want a string containing some number of US dollars? – JeremyP Jan 25 '23 at 10:25

3 Answers3

15

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

    ; INT 21 - DOS 1+ - CREATE OR TRUNCATE FILE USING FCB
    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.

user3840170
  • 23,072
  • 4
  • 91
  • 150
  • Answer can be improved: at some point ^ was added to escape. When was that? – Joshua Dec 12 '21 at 22:29
  • 1
    No earlier than in Windows NT, maybe OS/2. No version of MS-DOS supported it. – user3840170 Dec 12 '21 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 '22 at 12:27
14

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.

Raffzahn
  • 222,541
  • 22
  • 631
  • 918
  • 2
    In bash they resolve the ambiguities by adding brackets ${var} – phuclv Aug 14 '18 at 00: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 '18 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 '18 at 13:28
  • / was used for options in existing programs so \ was most likely chosen to avoid breaking those. – Thorbjørn Ravn Andersen Sep 29 '18 at 23:05
  • 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). – user3840170 Sep 02 '20 at 08: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 02 '20 at 18:49
  • @DrSheldon: I wonder if there would have been any difficulty using colons as a path specifier (as was done with the later Macintosh), and saying that relative paths are those that either don't contain any colons, or start with either a colon or dot-colon. – supercat Sep 08 '20 at 17:12
  • it certainly gives the impression that they tried to impose their standards with , %, damn drives, CRLF, and failed against Linux/Unix and now we're stuck with both standards... Truly visionnary. Fortunately they didn't invent their flavour of regexes too... – Jean-François Fabre Jan 24 '23 at 19:56
  • 1
    "Impose their standards" seems a bit biased. Why should the designers of system A constrain their choices because of what the designers of system B might have done? Why not complain that Unix did not follow the pre-existing CRLF standard used on DEC systems? – dave Jan 12 '24 at 01:59
1

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.

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
Igor Skochinsky
  • 1,780
  • 11
  • 23