我一直在写一些批处理文件,然后我看到了这个用户指南,它提供了很多信息。它告诉我的一件事是,行不仅可以用REM进行注释,还可以用::进行注释。它说:

批处理代码中的注释可以通过使用双冒号来生成,这比使用REM命令更好,因为标签在重定向符号之前被处理。::<remark>不会引起任何问题,但是rem <remark>会产生错误。

那么,为什么我看到的大多数指南和示例都使用REM命令呢?::适用于所有版本的Windows吗?


REM是在批处理文件中嵌入注释的有文档支持的方法。


::本质上是一个永远不能跳转到的空白标签,而REM是一个实际的命令,它什么也不做。在这两种情况下(至少在Windows 7上),重定向操作符的存在都不会导致问题。

然而,在某些情况下,::在块中表现不佳,不是作为标签而是作为某种驱动器号进行解析。我不太清楚具体在哪里,但仅此一点就足以让我只使用快速眼动。它是在批处理文件中嵌入注释并得到支持的方法,而::仅仅是特定实现的工件。


下面是一个例子,其中::在FOR循环中产生一个问题。

这个例子在桌面上的test.bat文件中不起作用:

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

虽然这个例子将正确地作为注释工作:

@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

问题似乎出现在试图将输出重定向到文件时。我的最佳猜测是,它将::解释为一个名为:echo的转义标签。


REM评论

REM可以注释一个完整的行,也可以注释行尾的多行插入号,如果它不是第一个标记的结束的话。

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后面跟着一些字符。:\/=的工作方式有点不同,它不注释&号,所以你可以把它用作内联注释。

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

但为了避免现有文件(如REM、REM.bat或REM;.bat)出现问题,应该只使用经过修改的版本。

REM^;<space>Comment

对于角色来说;也允许有;,:\/=

REM大约比::慢6倍(在Win7SP1上测试,有100000条注释行)。 对于正常使用来说,这并不重要(58µs vs 360µs /注释行)

评论:

A::总是执行行结束插入符。

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

标签和注释标签::在括号块中有一个特殊的逻辑。 它们总是跨越两行SO: goto命令不工作。 因此,不建议在括号块中使用它们,因为它们通常会导致语法错误。

使用ECHO ON显示REM行,但不显示注释为::

两者都不能真正注释掉该行的其余部分,因此简单的%~将导致语法错误。

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

但是REM能够在早期阶段停止批处理解析器,甚至在特殊字符阶段完成之前。

@echo ON
REM This caret ^ is visible

您可以使用& rem或&::在命令行末尾添加注释。 这种方法有效,因为'&'在同一行上引入了一个新命令。

带有百分号%= comment =%的注释

存在一个带有百分号的注释样式。

实际上,这些是变量,但它们被展开为零。 但优点是它们可以放在同一行中,即使没有&。 等号保证了这样一个变量不存在。

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

对于批处理宏,建议使用百分比样式,因为它不会改变运行时行为,因为在定义宏时注释将被删除。

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

性能REM vs:: vs %= =%

简而言之:

::和%= =%似乎具有相同的性能 REM的时间比: 在块中,特别是循环中,只有REM消耗时间,但是::在解析块时从缓存块中删除,因此它不消耗时间

有关更多信息,请参阅SO:关于批处理*.bat文件中的注释和速度的问题


另一种替代方法是将注释表示为一个总是展开为零的变量展开。

变量名不能包含=,除了未记录的动态变量,比如 %=ExitCode%和%=C:%。任何变量名都不能在第一个位置后面包含=。所以我有时会使用下面的代码在括号内包含注释:

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

它也是一种合并内联注释的好方法

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

前导=不是必须的,但我喜欢它的对称性。

有两个限制:

1)注释不能包含%

2)评论不能包含:


当我意识到我可以使用label::来注释和注释代码时,REM对我来说只是看起来很难看。如前所述,双冒号在()阻塞代码中使用时可能会导致问题,但我发现了一种变通方法,即在标签::和:空格之间交替使用

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

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

它不像REM那样丑陋,而且实际上为你的代码添加了一点风格。

所以在代码块之外,我使用::,在代码块内部,我在::和:之间交替。

顺便说一下,对于大量注释,比如批处理文件的头,您可以通过浏览注释来完全避免使用特殊命令和字符。这让你可以使用任何你想要的方法或风格的标记,尽管事实是,如果CMD实际上试图处理这些行,它会抛出一个嘘声。

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

使用任何你想要的符号*,@等等。


James K,我很抱歉我说错了很多。我所做的测试如下:

@ECHO OFF
(
  :: But
   : neither
  :: does
   : this
  :: also.
)

这符合您对交替的描述,但失败于“)was unexpected at This time.”错误消息。

我今天做了一些进一步的测试,发现交替不是关键,但关键似乎是有偶数行,没有任何两行在一行以双冒号(::)开始,也没有双冒号结束。考虑以下几点:

@ECHO OFF
(
   : But
   : neither
   : does
   : this
   : cause
   : problems.
)

这个工作!

但还要考虑到这一点:

@ECHO OFF
(
   : Test1
   : Test2
   : Test3
   : Test4
   : Test5
   ECHO.
)

当以命令结尾时,注释数量为偶数的规则似乎并不适用。

不幸的是,这只是奇怪的足够,我不确定我想要使用它。

实际上,最好的解决方案,也是我能想到的最安全的解决方案是,如果一个像notepad++这样的程序将REM读取为双冒号,然后在保存文件时将双冒号写入REM语句。但我不知道这样一个程序,我也不知道任何notepad++的插件,这样做。


好问题……我也一直在寻找这个功能…

经过几次测试和技巧,似乎更好的解决方案是更明显的…

——>最好的方法,我发现这样做,防止解析器完整性失败,是重用REM:

echo this will show until the next REM &REM this will not show

你也可以使用多行“NULL LABEL”技巧… (不要忘记行末的^以保持连贯性)

::(^
this is a multiline^
comment... inside a null label!^
dont forget the ^caret at the end-of-line^
to assure continuity of text^ 
)

关于这个话题的一个非常详细和分析性的讨论可以在这个页面上找到

它有示例代码和不同选项的优点/缺点。


该页告诉我们,在某些限制条件下,使用“::”会更快 只是选择时要考虑的一点


下面的回答试图对本页上许多很棒的答案进行实用主义的总结:

杰布的回答很好,值得特别提一下,因为它真的很深入,涵盖了许多边缘情况。 值得注意的是,他指出一个错误构造的变量/参数引用,比如%~,可以破坏下面的任何解——包括REM行。


全行注释——唯一直接支持的样式:

REM(或其大小写变体)是唯一的官方评论结构,也是最安全的选择——参见Joey的有用答案。 ::是一个(广泛使用的)黑客,它有优点和缺点: 优点: 视觉上的独特性,以及可能的打字便捷性。 速度,尽管这可能不太重要——看看jeb的精彩回答和Rob van der Woude的精彩博客文章。 缺点: 在(…)块内部,::可以破坏命令,安全使用的规则是限制性的,不容易记住-见下文。


如果你想使用::,你有这些选择:

或者:为了安全起见,在(…)块中创建一个异常并在那里使用REM,或者完全不在(…)块中放置注释。 或者:记住安全使用::inside(…)的痛苦的限制性规则,这些规则总结在下面的代码片段中:

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

模仿其他注释样式——内联和多行:

注意,批处理语言不直接支持这些样式,但是可以进行模拟。


内联注释:

*下面的代码片段使用ver作为任意命令的替身,以便于实验。 *要使SET命令在内联注释中正确工作,请双引号name=value部分;例如,SET "foo=bar".[1]

在这种情况下,我们可以区分两个子类型:

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.


多行(全行块)注释:

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.


在批处理文件中注释有多种方法

1)使用快速眼动

这是官方的做法。它的执行时间显然比::要长,尽管它显然会在插入符号被处理之前提前停止解析。百分比展开发生在rem和::被识别之前,因此如果百分比存在,不正确的百分比使用,例如%~将导致错误。在代码块的任何地方使用都是安全的。

2)使用标签:,::或:;等。

For :: comment, ': comment' is an invalid label name because it begins with an invalid character. It is okay to use a colon in the middle of a label though. If a space begins at the start of label, it is removed : label becomes :label. If a space or a colon appears in the middle of the label, the rest of the name is not interpreted meaning that if there are two labels :f:oo and :f rr, both will be interpreted as :f and only the later defined label in the file will be jumped to. The rest of the label is effectively a comment. There are multiple alternatives to ::, listed here. You can never goto or call a ::foo label. goto :foo and goto ::foo will not work.

They work fine outside of code blocks but after a label in a code block, invalid or not, there has to be a valid command line. :: comment is indeed another valid command. It interprets it as a command and not a label; the command has precedence. Which is the command to cd to the :: volume, which will work if you have executed subst :: C:\, otherwise you get a cannot find the volume error. That's why :; is arguably better because it cannot be interpreted in this way, and therefore is interpreted as a label instead, which serves as the valid command. This is not recursive, i.e, the next label does not need a command after it. That's why they come in twos.

你需要在标签后面提供一个有效的命令,例如echo something。代码块中的标签必须至少带有一个有效命令,因此行是成对的。如果下一行有空格或右括号,你会得到一个意外的错误。如果在两个::行之间有一个空格,你将得到一个无效的语法错误。

你也可以像这样在::注释中使用插入符:

@echo off

echo hello
(
   :;(^
   this^
   is^
   a^
   comment^
   )
   :;
)
   :;^
   this^
   is^
   a^
   comment
   :;
) 

但你需要后面的:;出于上述原因。

@echo off

(
echo hello
:;
:; comment
:; comment
:;
)
echo hello

只要是偶数就可以。这无疑是最好的评论方式——用4行:;:;你不会得到任何需要使用2> nul或subst:: C:\来抑制的错误。你可以使用subst:: C:\来消除卷未发现错误,但这意味着你必须在代码中加入C:,以防止你的工作目录变成::\。

在一行的末尾注释 Command &::或Command & rem注释,但仍然必须是一个偶数,就像这样:

@echo off

(
echo hello & :;yes
echo hello & :;yes
:;
)

echo hello

第一个回显hello &:;yes在下一行有一个有效的命令,但第二个&:;yes没有,所以它需要一个,即:;。

3)使用无效的环境变量

%= comment =%. In a batch file, environment variables that are not defined are removed from the script. This makes it possible to use them at the end of a line without using &. It is custom to use an invalid environment variable i.e. one that contains an equals sign. The extra equals is not required but makes it look symmetrical. Also, variable names starting with "=" are reserved for undocumented dynamic variables. Those dynamic variables never end with "=", so by using an "=" at both the start and end of the comment, there is no possibility of a name clash. The comment cannot contain % or :.

@echo off 
echo This is an example of an %= Inline Comment =% in the middle of a line.

4)作为命令,将stderr重定向为nul

@echo off
(
echo hello
;this is a comment 2> nul
;this is another comment  2> nul
)

5)在文件末尾,未闭括号后面的所有内容都是注释

@echo off
(
echo hello
)

(this is a comment
this is a comment
this is a comment