Which comment style should I use in batch files?

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.


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:

      • Visual distinctiveness and, possibly, ease of typing.
      • Speed, although that will probably rarely matter - see jeb's great answer and Rob van der Woude's excellent blog post.
    • 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.


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


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 :