在批处理文件中应使用哪种注释样式?


283

我一直在编写一些批处理文件,并且遇到了本用户指南,该指南非常有用。它向我展示的一件事是,不仅可以用REM,也有::。它说:

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

那么,为什么我看到的大多数指南和示例都使用该REM命令?是否::在所有版本的Windows的工作?


3
只是为了记录在案,当“REM”是在Windows 98下使用注释出了一条符合重定向我已经看到了问题
挖掘机

6
顺便说一句,与@Digger的注释一致:链接的指南适用于DOScommand.exe),不适用于Windows 2000及更高版本上cmd.exeNT命令处理器。rem <remark>在后者(至少从 Windows XP开始)中可以很好地工作,并且REM是官方的限制词,也是总体上最安全的选择;尽管::它有其优点,但最终它是一个在内部(…)块中有问题的黑客(如此处许多答案所述)。
mklement0


1
那么,什么情况导致REM会导致错误呢?
TS

Answers:


359

tl; dr: REM是在批处理文件中嵌入注释的记录和受支持的方法。


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

但是,::众所周知,在某些情况下,block的行为不正常,不是作为标签而是作为某种驱动器号进行解析。我对确切的位置有些模糊,但仅此一个就足以让我REM独占使用。这是在批处理文件中嵌入注释的文档化和受支持的方式,而::仅仅是特定实现的产物。


这是::一个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


1
@Firedan:批处理文件的名称及其位置是否相关(以及重定向到的文件的名称和位置?)。否则,最好简化该示例。
乔伊,

15
添加tl; dr
makoshichi的好方法'16

2
如果行中延迟使用变量,则::将导致一些错误消息,例如,找不到特定的磁盘驱动器.....因此,最好使用REM。
朱Chu

2
::注释已解析,特殊字符如> | 结束注释,并且以下文本不被注释。
莫什

4
@mosh是正确的。例如,%VAR%变量被扩展。假设您有(错误)set TARGET=C:\Program Files (x86)\"foo.exe",并且在DO(..)表达式内部,:: echo %TARGET%您将得到一个错误,因为对整个表达式求值之前(x86)扩展将被扩展,从而导致一个无效的表达式和非常莫名的错误(在这种情况下,“ \ Microsoft此时是意外的” ”。你甚至都不需要或者在你的表达。不过,A 不是真正的评论。DO(..)|>::REM
亚伯

161

用REM评论

REM如果不是第一个标记的末尾,则A 可以标记整行,也可以在行末标记多行插入号。

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

但为避免现有文件(如)出现问题REMREM.batREM;.bat仅应使用经过修改的变体。

REM^;<space>Comment

而对于角色;也被允许;,:\/=

REM 比(在100000条注释行的Win7SP1上测试)慢6倍::
对于正常使用而言,这并不重要(每个注释行58µs与360µs)

用::留言

A ::始终执行行结束插入符。

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

标签和注释标签 ::在括号块中具有特殊的逻辑。
它们总是跨越两行,所以:goto命令不起作用
因此,不建议将它们用于括号块,因为它们通常是语法错误的原因。

随着ECHO ON一个REM线被示出,但没有一个符合注释::

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

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

但是,REM可以在早期阶段停止批处理解析器,即使在特殊字符阶段完成之前也是如此。

@echo ON
REM This caret ^ is visible

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

带百分号的评论%=评论=%

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

实际上,这些是变量,但没有扩展。
但优点是即使没有也可以将它们放在同一行中&
等号确保了这样的变量不存在。

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

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

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

2
应当注意,%=注释带有引号是挑剔的,即,set foo=bar %=baz导致foo扩展为bar %=baz,就像一样set foo="bar" %=baz,而仅set "foo=bar" %=baz导致foo扩展bar为预期的。
LastStar007

3
@ LastStar007:通常set "foo=bar"值得推荐使用引号样式,因为它是最可靠的形式,清楚地界定了值。您所描述的问题是行为固有set,并非特定于%= … =%注释:除非使用"var=val"引号,否则应set考虑=该值之后的所有内容,包括尾随空格(直到行尾或(如果适用)行尾)。下一个内联命令)。
mklement0

28

另一种选择是将注释表示为始终扩展为空的变量扩展。

变量名不能包含=,除了未记录的动态变量(如
%=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)评论不能包含 :


大声笑!使其成为一个超大变量!天才!%=ExitCode%?整齐。每天学些新东西!
詹姆斯·K

您暗示尾随=是必要的。但事实并非如此。
詹姆斯·K

4
@JamesK-我使用结尾,=以便%= ExitCode =%之类的内容是“注释”,而不是动态变量。我更喜欢使用一种始终有效的样式(当然,答案底部注明的限制除外)。
dbenham 2012年

有关动态变量的信息,请参见stackoverflow.com/a/20169219/1689714(例如,%= ExitCode%%= ExitCodeAscii%%= C:%%= D:%%__ CD __%等),它们的含义,含义设置等。
基隆哈迪

25

当我意识到我可以使用标签::进行注释和注释掉代码后REM,对我来说看起来很丑陋。如前所述,在()阻止代码中使用双冒号会引起问题,但是我发现了一种解决方法,即在标签:::space

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

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

它不像丑陋REM,实际上为您的代码添加了一些样式。

因此,在代码块之外,我使用::它们,而在代码块中,我在::和之间切换:

顺便说一下,对于大量的注释,如批处理文件的标题中的注释,您只需goto查看注释即可完全​​避免特殊的命令和字符。这使您可以使用所需的任何方法或样式的标记,尽管事实是,即使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
.
.
.

使用您希望使用的符号*@等等。


您如何处理/?开关以使其打印此菜单?
hoang 2013年

1
@hoang setlocal ENABLEDELAYEDEXPANSION <NEWLINE>设置var =%〜1 <NEWLINE>回显第一个参数为%1 <NEWLINE> IF!VAR!==“ /?” (转到用法)<NEWLINE>:用法<NEWLINE> echo blah blah .. <NEWLINE>
GL201413

16
当您插入或删除一行时,单冒号和双冒号的交替必须让人头疼。

@ GL2014基本上是在说“您打印菜单”。您的示例代码要求在使用说明的每一行之前添加echo前缀。James K的答案在某种程度上误导了人们的印象,即它暗示着可以使用某种方式来打印书面使用说明。
Timbo '18

1
@Timbo我为此答案写了一个子程序(:PrintHelp),确实可以实现@hoang的要求。我使用<HELP>和</ HELP>作为标记,但是您可以使用任何适合您的标记。
cdlvcdlv

21

该答案尝试对本页上的许多出色答案进行实用的总结

jeb的出色答案值得特别提及,因为它的确深入,涵盖了许多极端情况。
他特别指出,构造错误的变量/参数参考例如%~可能会破坏下面的任何解决方案,包括REMline


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

  • REM(或大小写不同)是唯一的官方评论结构,也是最安全的选择 -请参见Joey的有用答案

  • ::是一个(广泛使用的)hack,它具有优点和缺点

    • 优点

    • 缺点

      • (...)块内部,::可以破坏命令,并且安全使用规则是限制性的并且不容易记住 -参见下文。

如果您确实想使用::,则有以下选择:

  • 要么:为了安全起见,请在(...)块内例外并在其中使用REM,或者不要将注释完全放在内部 (...)
  • 或者:记住安全使用::内部的严格限制规则,(...)以下代码段对此进行了总结:
@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注释(行尾),可以放在命令后,并且始终延伸到行尾(同样,由jeb的回答提供):

    • ver & REM <comment>利用了以下事实:这REM是有效命令,&可用于在现有命令之后放置一个附加命令。
    • ver & :: <comment>可以使用(...),但实际上仅在外可用,因为它的安全使用比::单机使用受到更多限制。
  • 行内注释,放置一行中的多个命令之间,或者最好放在给定命令的内部
    行内注释是最灵活的(单行)形式,根据定义也可以用作EOL注释。

    • ver & REM^. ^<comment^> & ver允许在命令之间插入注释(同样,由jeb的answer表示感谢),但请注意如何<以及>需要将其^转义,因为以下字符。不能原样使用:< > |(而非转义&&&||启动下一个命令)。

    • %= <comment> =%,在详细dbenham最伟大的答案,是最灵活的形式,因为它可以放在里面的命令(参数中)
      它利用可变扩展语法的方式来确保表达式始终扩展为空字符串 - 只要注释文本既不包含%也不包含:
      Like REM,则%= <comment> =%(...)块的内部和外部都可以很好地工作,但是在视觉上更具特色;唯一的缺点是难以键入,语法上更容易出错,并且不为人所知,这可能会妨碍对使用该技术的源代码的理解。


多行(整行块)注释

  • James K的答案显示了如何使用goto语句和标签来界定任意长度和内容的多行注释(在他的情况下,他使用该注释来存储使用情况信息)。

  • Zee的答案显示了如何使用“空标签”创建多行注释,尽管必须注意用终止所有内部行^

  • 罗布的van der Woude的博客文章提到了另一种有些模糊的选项,使您可以结束与注释行任意数量的文件:一个开放(导致自带之后被忽略的一切,只要它不包含(非^-escaped)),即只要该块未关闭


[1] 在诸如之类的命令中,有必要使用SET "foo=bar"定义变量(即,在名称和=组合的值两边加上双引号)SET "foo=bar" & REM Set foo to bar.,以确保遵循预期的变量值(在这种情况下,直到下一个命令)单个空间)不会意外地成为其中的一部分。
(顺便说一句:SET foo="bar"不仅不会避免该问题,还会使双引号成为value的一部分)。
请注意,此问题是值后面的意外尾随空格固有的SET,甚至适用于此,因此建议始终使用该方法。SET "foo=bar"


7

页面告诉您,在某些约束条件下,使用“ ::”会更快。


2
的确如此,至少对于Win7SP1而言,它::可以比REM
jeb 2013年

4

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

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

->我发现做到这一点的最佳方法(防止解析器完整性失败)是重用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^ 
)

3

詹姆斯·K(James K),对不起,我所说的相当一部分内容是错误的。我所做的测试如下:

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

这符合您对交替的描述,但失败,并带有“),这在此时是意外的。” 错误信息。

我今天进行了一些进一步的测试,发现交替不是关键,但看来关键是行数是偶数,连续没有两行以双冒号(::)开头而不是以双冒号结尾。考虑以下:

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

这可行!

还要考虑一下:

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

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

不幸的是,这只是足以使我不确定是否要使用它。

确实,最好的解决方案,也是我想到的最安全的解决方案,是如果类似Notepad ++的程序将REM读为双冒号,然后在保存文件时将双冒号写回为REM语句。但是我不知道这样的程序,也不知道任何用于Notepad ++的插件。


2

有关该主题的非常详细且分析性的讨论,请参见 页面上

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


1
您应该总结答案中提供的链接的内容。否则,它称为“仅链接答案”,如果链接消失,则完全没有用。在这种情况下,指向的页面很幽默,因为它是基于优化从慢速软盘读取批处理文件的速度来做出选择的:)
GreenAsJade 2015年

0

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

1)使用雷姆

这是官方的方式。::尽管显然要在处理插入符号之前就停止解析,但执行显然要比花费更长的时间。百分比扩展发生在rem之前并::已确定,因此百分比使用不正确,即%~如果存在百分比,则会导致错误。在代码块中的任何地方都可以安全使用。

2)使用标签::::;

对于:: comment,':comment'是无效的标签名称,因为它无效字符开头。可以在标签中间使用冒号。如果空格从标签的开头开始,则它将: label变为:label。如果标签的中间出现空格或冒号,则不会解释名称的其余部分,这意味着如果有两个标签:f:oo:f rr,则两个都将解释为,:f并且仅跳至文件中后来定义的标签。标签的其余部分实际上是注释。有多种替代品::,上市这里。你永远不能gotocall一个::foo标签。goto :foo并且goto ::foo将无法正常工作。

它们在代码块之外可以正常工作,但是在代码块中的标签之后(不管是否有效),必须有一个有效的命令行。:: comment确实是另一个有效的命令。它将其解释为命令而不是标签;该命令具有优先权。cd到该::卷的命令,如果已执行subst :: C:\,该命令将起作用,否则将出现找不到卷的错误。这就是为什么:;可以说更好的原因,因为它不能以这种方式进行解释,因此可以解释为标签,它用作有效命令。这不是递归的,即下一个标签不需要后面的命令。这就是为什么它们合二为一。

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

您还可以在::注释中使用插入符号运算符,如下所示:

@echo off

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

但是:;由于上述原因,您需要尾随。

@echo off

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

只要有偶数就可以。无疑这是最好的评论方式-用4行和:;。随着:;你不要用抑制需要的任何错误2> nulsubst :: C:\。您可以subst :: C:\用来使“找不到卷”错误消失,但这意味着您还必须在代码中放入C:,以防止工作目录变为::\

要在一行的末尾添加注释,您可以执行 command &::command & rem comment,但仍然必须有一个偶数,如下所示:

@echo off

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

echo hello

第一echo hello & :;yes行在下一行有一个有效命令,但第二行& :;yes则没有,因此需要一个命令,即:;

3)使用无效的环境变量

%= comment =%。在批处理文件中,未定义的环境变量将从脚本中删除。这样就可以在一行的末尾使用它们而不使用&。通常使用无效的环境变量,即包含等号的变量。多余的等号不是必需的,但使它看起来对称。同样,以“ =”开头的变量名称保留给未记录的动态变量。这些动态变量永远不会以“ =”结尾,因此通过在注释的开头和结尾都使用“ =”,就不会出现名称冲突。注释不能包含%:

@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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.