Which comment style should I use in batch files?

354,749

Solution 1

tl;dr: REM is the documented and supported way to embed comments in batch files.


:: is essentially a blank label that can never be jumped to, whereas REM is an actual command that just does nothing. In neither case (at least on Windows 7) does the presence of redirection operators cause a problem.

However, :: is known to misbehave in blocks under certain circumstances, being parsed not as a label but as some sort of drive letter. I'm a little fuzzy on where exactly but that alone is enough to make me use REM exclusively. It's the documented and supported way to embed comments in batch files whereas :: is merely an artifact of a particular implementation.


Here is an example where :: produces a problem in a FOR loop.

This example will not work in a file called test.bat on your desktop:

@echo off
for /F "delims=" %%A in ('type C:\Users\%username%\Desktop\test.bat') do (
    ::echo hello>C:\Users\%username%\Desktop\text.txt
)
pause

While this example will work as a comment correctly:

@echo off
for /F "delims=" %%A in ('type C:\Users\%username%\Desktop\test.bat') do (
    REM echo hello>C:\Users\%username%\Desktop\text.txt
)
pause

The problem appears to be when trying to redirect output into a file. My best guess is that it is interpreting :: as an escaped label called :echo.

Solution 2

Comments with REM

A REM can remark a complete line, also a multiline caret at the line end, if it's not the end of the first token.

REM This is a comment, the caret is ignored^
echo This line is printed

REM This_is_a_comment_the_caret_appends_the_next_line^
echo This line is part of the remark

REM followed by some characters .:\/= works a bit different, it doesn't comment an ampersand, so you can use it as inline comment.

echo First & REM. This is a comment & echo second

But to avoid problems with existing files like REM, REM.bat or REM;.bat only a modified variant should be used.

REM^;<space>Comment

And for the character ; is also allowed one of ;,:\/=

REM is about 6 times slower than :: (tested on Win7SP1 with 100000 comment lines).
For a normal usage it's not important (58µs versus 360µs per comment line)

Comments with ::

A :: always executes a line end caret.

:: This is also a comment^
echo This line is also a comment

Labels and also the comment label :: have a special logic in parenthesis blocks.
They span always two lines SO: goto command not working.
So they are not recommended for parenthesis blocks, as they are often the cause for syntax errors.

With ECHO ON a REM line is shown, but not a line commented with ::

Both can't really comment out the rest of the line, so a simple %~ will cause a syntax error.

REM This comment will result in an error %~ ...

But REM is able to stop the batch parser at an early phase, even before the special character phase is done.

@echo ON
REM This caret ^ is visible

You can use &REM or &:: to add a comment to the end of command line. This approach works because '&' introduces a new command on the same line.

Comments with percent signs %= comment =%

There exists a comment style with percent signs.

In reality these are variables but they are expanded to nothing.
But the advantage is that they can be placed in the same line, even without &.
The equal sign ensures, that such a variable can't exists.

echo Mytest
set "var=3"     %= This is a comment in the same line=%

The percent style is recommended for batch macros, as it doesn't change the runtime behaviour, as the comment will be removed when the macro is defined.

set $test=(%\n%
%=Start of code=% ^
echo myMacro%\n%
)

Performance REM vs :: vs %= =%

In short:

  • :: and %= =% seems to have the same performance
  • REM takes ~ 50% more time than ::
  • In blocks, especially loops only REM consumes time, but :: is removed from the cached block when the block is parsed, therefore it consumes no time

For more info see SO: Question about Comments in Batch *.bat files and speed

Solution 3

Another alternative is to express the comment as a variable expansion that always expands to nothing.

Variable names cannot contain =, except for undocumented dynamic variables like
%=ExitCode% and %=C:%. No variable name can ever contain an = after the 1st position. So I sometimes use the following to include comments within a parenthesized block:

::This comment hack is not always safe within parentheses.
(
  %= This comment hack is always safe, even within parentheses =%
)

It is also a good method for incorporating in-line comments

dir junk >nul 2>&1 && %= If found =% echo found || %= else =% echo not found

The leading = is not necessary, but I like if for the symmetry.

There are two restrictions:

1) the comment cannot contain %

2) the comment cannot contain :

Solution 4

This answer attempts a pragmatic summary of the many great answers on this page:

jeb's great answer deserves special mention, because it really goes in-depth and covers many edge cases.
Notably, he points out that a misconstructed variable/parameter reference such as %~ can break any of the solutions below - including REM lines.


Whole-line comments - the only directly supported style:

  • REM (or case variations thereof) is the only official comment construct, and is the safest choice - see Joey's helpful answer.

  • :: is a (widely used) hack, which has pros and cons:

    • Pros:

    • Cons:

      • Inside (...) blocks, :: can break the command, and the rules for safe use are restrictive and not easy to remember - see below.

If you do want to use ::, you have these choices:

  • Either: To be safe, make an exception inside (...) blocks and use REM there, or do not place comments inside (...) altogether.
  • Or: Memorize the painfully restrictive rules for safe use of :: inside (...), which are summarized in the following snippet:
@echo off

for %%i in ("dummy loop") do (

  :: This works: ONE comment line only, followed by a DIFFERENT, NONBLANK line.
  date /t

  REM If you followed a :: line directly with another one, the *2nd* one
  REM would generate a spurious "The system cannot find the drive specified."
  REM error message and potentially execute commands inside the comment.
  REM In the following - commented-out - example, file "out.txt" would be
  REM created (as an empty file), and the ECHO command would execute.
  REM   :: 1st line
  REM   :: 2nd line > out.txt & echo HERE

  REM NOTE: If :: were used in the 2 cases explained below, the FOR statement
  REM would *break altogether*, reporting:
  REM  1st case: "The syntax of the command is incorrect."
  REM  2nd case: ") was unexpected at this time."

  REM Because the next line is *blank*, :: would NOT work here.

  REM Because this is the *last line* in the block, :: would NOT work here.
)

Emulation of other comment styles - inline and multi-line:

Note that none of these styles are directly supported by the batch language, but can be emulated.


Inline comments:

* The code snippets below use ver as a stand-in for an arbitrary command, so as to facilitate experimentation.
* To make SET commands work correctly with inline comments, double-quote the name=value part; e.g., SET "foo=bar".[1]

In this context we can distinguish two subtypes:

  • EOL comments ([to-the-]end-of-line), which can be placed after a command, and invariably extend to the end of the line (again, courtesy of jeb's answer):

    • ver & REM <comment> takes advantage of the fact that REM is a valid command and & can be used to place an additional command after an existing one.
    • ver & :: <comment> works too, but is really only usable outside of (...) blocks, because its safe use there is even more limited than using :: standalone.
  • Intra-line comments, which be placed between multiple commands on a line or ideally even inside of a given command.
    Intra-line comments are the most flexible (single-line) form and can by definition also be used as EOL comments.

    • ver & REM^. ^<comment^> & ver allows inserting a comment between commands (again, courtesy of jeb's answer), but note how < and > needed to be ^-escaped, because the following chars. cannot be used as-is: < > | (whereas unescaped & or && or || start the next command).

    • %= <comment> =%, as detailed in dbenham's great answer, is the most flexible form, because it can be placed inside a command (among the arguments).
      It takes advantage of variable-expansion syntax in a way that ensures that the expression always expands to the empty string - as long as the comment text contains neither % nor :
      Like REM, %= <comment> =% works well both outside and inside (...) blocks, but it is more visually distinctive; the only down-sides are that it is harder to type, easier to get wrong syntactically, and not widely known, which can hinder understanding of source code that uses the technique.


Multi-line (whole-line block) comments:

  • James K's answer shows how to use a goto statement and a label to delimit a multi-line comment of arbitrary length and content (which in his case he uses to store usage information).

  • Zee's answer shows how to use a "null label" to create a multi-line comment, although care must be taken to terminate all interior lines with ^.

  • Rob van der Woude's blog post mentions another somewhat obscure option that allows you to end a file with an arbitrary number of comment lines: An opening ( only causes everything that comes after to be ignored, as long as it doesn't contain a ( non-^-escaped) ), i.e., as long as the block is not closed.


[1] Using SET "foo=bar" to define variables - i.e., putting double quotes around the name and = and the value combined - is necessary in commands such as SET "foo=bar" & REM Set foo to bar., so as to ensure that what follows the intended variable value (up to the next command, in this case a single space) doesn't accidentally become part of it.
(As an aside: SET foo="bar" would not only not avoid the problem, it would make the double quotes part of the value).
Note that this problem is inherent to SET and even applies to accidental trailing whitespace following the value, so it is advisable to always use the SET "foo=bar" approach.

Solution 5

After I realized that I could use label :: to make comments and comment out code REM just looked plain ugly to me. As has been mentioned the double-colon can cause problems when used inside () blocked code, but I've discovered a work-around by alternating between the labels :: and :space

:: This, of course, does
:: not cause errors.

(
  :: But
   : neither
  :: does
   : this.
)

It's not ugly like REM, and actually adds a little style to your code.

So outside of code blocks I use :: and inside them I alternate between :: and :.

By the way, for large hunks of comments, like in the header of your batch file, you can avoid special commands and characters completely by simply gotoing over your comments. This let's you use any method or style of markup you want, despite that fact that if CMD ever actually tried to processes those lines it'd throw a hissy.

@echo off
goto :TopOfCode

=======================================================================
COOLCODE.BAT

Useage:
  COOLCODE [/?] | [ [/a][/c:[##][a][b][c]] INPUTFILE OUTPUTFILE ]

Switches:
       /?    - This menu
       /a    - Some option
       /c:## - Where ## is which line number to begin the processing at.
         :a  - Some optional method of processing
         :b  - A third option for processing
         :c  - A forth option
  INPUTFILE  - The file to process.
  OUTPUTFILE - Store results here.

 Notes:
   Bla bla bla.

:TopOfCode
CODE
.
.
.

Use what ever notation you wish *'s, @'s etc.

Share:
354,749
MikeFHay
Author by

MikeFHay

UK-based software developer working primarily on back-end Java projects. https://github.com/MikeFHay

Updated on July 17, 2022

Comments

  • MikeFHay
    MikeFHay almost 2 years

    I've been writing some batch files, and I ran into this user guide, which has been quite informative. One thing it showed me was that lines can be commented not just with REM, but also with ::. It says:

    Comments in batch code can be made by using a double-colon, this is better than using the REM command because labels are processed before redirection symbols. ::<remark> causes no problems but rem <remark> produces errors.

    Why then, do most guides and examples I see use the REM command? Does :: work on all versions of Windows?

    • Digger
      Digger over 8 years
      Just for the record, I've seen problems when "REM" is used to comment out a line with redirection under Windows 98.
    • mklement0
      mklement0 over 7 years
      As an aside, in line with @Digger's comment: The linked guide is for DOS (command.exe), not cmd.exe, the NT command processor as found on Windows 2000 onward. rem <remark> works just fine in the latter (since at least Windows XP), and REM is the official constracnt and the safest choice overall; while :: has its advantages, it is ultimately a hack that is problematic inside (…) blocks (as discussed in many answers here).
    • T.Todua
      T.Todua about 6 years
    • T.S
      T.S about 6 years
      So, what situation with REM causes errors exactly?
  • James K
    James K over 11 years
    LOL! Make it one oversized variable! Genius! %=ExitCode%? Neat. Learn something new every day!
  • James K
    James K over 11 years
    You imply that the trailing = is necessary. But it does not seem to be.
  • dbenham
    dbenham over 11 years
    @JamesK - I use the trailing = so that something like %=ExitCode=% is a "comment" and not a dynamic variable. I prefer to use a style that always works (except for limitations noted at bottom of answer of course).
  • hoang
    hoang almost 11 years
    How do you handle the /? switch to make it print this menu ?
  • GL2014
    GL2014 almost 11 years
    @hoang setlocal ENABLEDELAYEDEXPANSION <NEWLINE> set var=%~1 <NEWLINE> echo first param is %1 <NEWLINE> IF !VAR!=="/?" ( GOTO USAGE ) <NEWLINE> :USAGE <NEWLINE> echo blah blah.. <NEWLINE>
  • Admin
    Admin about 10 years
    Alternating single and double colons must be a headache when you insert or delete a row.
  • LastStar007
    LastStar007 almost 9 years
    It should be noted that %= comments are finicky with quotation marks, i.e. set foo=bar %=baz results in foo expanding to bar %=baz, as does set foo="bar" %=baz, whereas only set "foo=bar" %=baz results in foo expanding to bar as intended.
  • GreenAsJade
    GreenAsJade over 8 years
    You should summarize the contents of links provided in answers. Otherwise it's called a "link only answer", and is completely useless if the link disappears. In this case, the page pointed to is rather humorous in that it makes its choice based on optimising reading speed of batch files from slow floppy disk :)
  • jeb
    jeb over 8 years
    This is true, at least for Win7SP1, :: can be 6 times faster than REM
  • Rich
    Rich almost 8 years
    @Firedan: Is the name of the batch file and its location relevant (along with the name and location of the file to redirect to?). Otherwise it'd be nice to simplify the example.
  • makoshichi
    makoshichi almost 8 years
    Nice touch adding a tl;dr
  • Scott Chu
    Scott Chu almost 8 years
    If there's delayed variable usage in line, :: will cause some error messages, e.g. Can not find specificed disk driver..... So better use REM then.
  • mosh
    mosh over 7 years
    :: comments are parsed, and special characters like > | end the comment, and the following text is not commented.
  • mklement0
    mklement0 over 7 years
    @LastStar007: Always using quoting style set "foo=bar" is worth recommending in general, because it is the most robust form that clearly delimits the value. The issue you're describing is inherent in set's behavior, and not specific to %= … =% comments: unless you use "var=val" quoting, set considers everything that follows the = the value, including trailing whitespace (through the end of the line or, if applicable, the start of the next inline command).
  • Aisah Hamzah
    Aisah Hamzah over 6 years
    @mosh is right. For instance, %VAR% variables are expanded. Suppose you have (wrongly) set TARGET=C:\Program Files (x86)\"foo.exe", and inside a DO(..) expression you have :: echo %TARGET% you will get an error because the (x86) gets expanded before the whole expression gets evaluated, leading to an invalid DO(..) expression and very inexplicable errors (in this case "\Microsoft was unexpected at this time"). You don't even need | or > in your expression. A :: is not a real comment, REM is, though.
  • Sinjai
    Sinjai over 6 years
    Came here getting all kinds of errors from my batch script. Changed all of my :: comments to REM, everything started working. Was not aware :: was a hack!
  • Kieron Hardy
    Kieron Hardy about 6 years
    See stackoverflow.com/a/20169219/1689714 for an exploration of the dynamic variables (e.g. %=ExitCode% %=ExitCodeAscii% %=C:% %=D:% %__CD__% etc.), what they mean, how they are set, etc..
  • Tim Sparkles
    Tim Sparkles about 6 years
    @GL2014 basically you're saying "you don't print this menu". Your example code requires prefixing echo to each line of the usage notes. James K's answer is misleading to the extent that it suggests there is some way to print the usage notes as written .
  • cdlvcdlv
    cdlvcdlv almost 6 years
    @Timbo I wrote a subroutine (:PrintHelp) for this answer that indeed does what @hoang asks for. I use <HELP> and </HELP> as markers but you can use whatever suits you.
  • Serge Wautier
    Serge Wautier about 3 years
    Upvoted for mentioning %= comment =%. OK, I read the comments. Will use with care...
  • EvgenKo423
    EvgenKo423 almost 2 years
    In blocks, especially loops only REM consumes time, but :: is removed from the cached block when the block is parsed, therefore it consumes no time – probably not, otherwise it wouldn't cause errors like this.