Какой стиль комментария я должен использовать в пакетных файлах?


284

Я писал некоторые командные файлы и наткнулся на это руководство пользователя , которое было довольно информативным. Одна вещь, которую он показал мне, заключалась в том, что строки можно комментировать не толькоREM , но и с помощью ::. Это говорит:

Комментарии в пакетном коде могут быть сделаны с использованием двойной двоеточия, это лучше, чем использование команды REM, поскольку метки обрабатываются перед символами перенаправления. ::<remark>не вызывает проблем, но rem <remark>выдает ошибки.

Почему тогда большинство руководств и примеров, которые я вижу, используют REMкоманду? Работает ли ::на всех версиях Windows?


3
Просто для записи, я видел проблемы, когда «REM» используется, чтобы закомментировать строку с перенаправлением в Windows 98.
Digger

6
Кроме того, в соответствии с комментарием @ Digger: Связанное руководство предназначено для DOS ( command.exe), а не cmd.exeдля командного процессора NT , как в Windows 2000 и более поздних версиях. rem <remark>прекрасно работает в последнем (так по крайней мере , Windows XP), а также REMявляется официальным constracnt и самым безопасным выбором в целом; хотя и ::имеет свои преимущества, это в конечном итоге хак, который проблематичен внутри (…)блоков (как обсуждалось во многих ответах здесь).
mklement0


1
Итак, какая ситуация с REM вызывает ошибки именно?
TS

Ответы:


360

tl; dr: REM документированный и поддерживаемый способ встраивания комментариев в пакетные файлы.


::по сути, это пустая метка, к которой никогда нельзя перейти, тогда REMкак это реальная команда, которая ничего не делает. Ни в том, ни в другом случае (по крайней мере, в Windows 7) присутствие операторов перенаправления не вызывает проблемы.

Тем не менее, ::известно, что при определенных обстоятельствах в блоках он ведет себя не так, как синтаксический анализ, а не как буква диска. Я немного размышляю о том, где именно, но одного этого достаточно, чтобы заставить меня использовать 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: релевантно имя пакетного файла и его местоположение (вместе с именем и расположением файла для перенаправления?). В противном случае было бы неплохо упростить пример.
Джои


2
Если в строке указано задержанное использование переменной, :: вызовет некоторые сообщения об ошибках, например, Не удается найти определенный драйвер диска ..... Так что лучше используйте REM.
Скотт Чу

2
:: комментарии анализируются, а специальные символы, такие как> | конец комментария, а следующий текст не комментируется.
Мош

4
@ Мош прав. Например, %VAR%переменные раскрываются. Предположим, что у вас есть (ошибочно) set TARGET=C:\Program Files (x86)\"foo.exe", и внутри DO(..)имеющегося у :: echo %TARGET%вас выражения вы получите ошибку, потому что (x86)расширение раскрывается до того, как вычисляется все выражение, что приводит к недопустимому DO(..)выражению и очень необъяснимым ошибкам (в данном случае «\ Microsoft была неожиданной в это время " ) Вам даже не нужно |или >в вашем выражении. А ::это не настоящий комментарий REM, хотя.
Авель

161

Комментарии с REM

A 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 мкс против 360 мкс на строку комментария)

Комментарии с ::

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«s поведение, а не специфичен для %= … =%комментариев: если вы не использовать "var=val"квотирование, setсчитает все , что следует за =значение, в том числе конечных пробелов (до конца строки или, если это применимо, начала следующая встроенная команда).
mklement0

28

Другой альтернативой является выражение комментария как расширение переменной, которое всегда расширяется до нуля.

Имена переменных не могут содержать =, кроме недокументированных динамических переменных, таких как
%=ExitCode%и %=C:%. Имя переменной не может содержать =после 1-й позиции. Поэтому я иногда использую следующее для включения комментариев в блок в скобках:

::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) комментарий не может содержать :


РЖУНИМАГУ! Сделайте это одной большой переменной! Genius! %=ExitCode%? Ухоженная. Узнавайте что-то новое каждый день!
Джеймс К

Вы подразумеваете, что трейлинг =необходим. Но, похоже, нет.
Джеймс К

4
@JamesK - я использую трейлинг, =чтобы что-то вроде% = ExitCode =% было «комментарием», а не динамической переменной. Я предпочитаю использовать стиль, который всегда работает (за исключением ограничений, указанных в нижней части ответа, конечно).
Денбен

См. Stackoverflow.com/a/20169219/1689714 для исследования динамических переменных (например,% = ExitCode%% = ExitCodeAscii%% = C:%% = D:%% __ CD __% и т. Д.), Что они имеют в виду, как они установлены и т. д.
Kieron Hardy

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

Используйте любые обозначения, которые вы хотите *, @и т. Д.


Как вы справляетесь с /?переключателем, чтобы он печатал это меню?
Хоан

1
@hoang setlocal ENABLEDELAYEDEXPANSION <NEWLINE> set var =% ~ 1 <NEWLINE> первый параметр эха:% 1 <NEWLINE> IF! VAR! == "/?" (GOTO USAGE) <NEWLINE>: использование <NEWLINE> эхо, бла-бла .. <NEWLINE>
GL2014

16
Чередование одинарных и двойных двоеточий должно быть головной болью при вставке или удалении строки.

@ GL2014 вы говорите "вы не печатаете это меню". Ваш пример кода требует добавления префикса echo к каждой строке заметок об использовании. Ответ Джеймса К. вводит в заблуждение в той степени, в которой он предполагает, что есть какой-то способ напечатать заметки об использовании в том виде, как они написаны.
Тимбо

1
@ Тимбо Я написал подпрограмму ( :PrintHelp) для этого ответа, которая действительно выполняет то, что просит @hoang. Я использую <HELP> и </ HELP> в качестве маркеров, но вы можете использовать все, что вам подходит.
cdlvcdlv

21

В этом ответе делается попытка прагматического изложения многих замечательных ответов на этой странице:

Отличный ответ Джеба заслуживает отдельного упоминания, потому что он действительно углублен и охватывает многие крайние случаи.
В частности, он указывает на то, что неверно созданная ссылка на переменную / параметр, например, %~может нарушить любое из приведенных ниже решений, включая REMстроки .


Целостные комментарии - единственный напрямую поддерживаемый стиль:

  • REM(или варианты вариантов) - это единственная официальная конструкция комментария , и это самый безопасный выбор - см . полезный ответ Джои .

  • ::является (широко используемым) хаком , который имеет плюсы и минусы :

    • Плюсы :

    • Минусы :

      • Внутри (...)блоков ::может нарушаться команда , а правила безопасного использования являются ограничительными и их нелегко запомнить - см. Ниже.

Если вы действительно хотите использовать:: , у вас есть такой выбор:

  • Либо : для безопасности сделайте исключение внутри (...)блоков и используйте их 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 ([to-the-] end-of-line), которые могут быть помещены после команды и неизменно продолжаться до конца строки (опять же, любезность ответа Джеба ):

    • ver & REM <comment>использует тот факт, что REMэто допустимая команда и &может использоваться для размещения дополнительной команды после существующей.
    • ver & :: <comment>тоже работает, но на самом деле его можно использовать только вне (...)блоков , потому что его безопасное использование там еще более ограничено, чем использование ::автономного.
  • Внутристрочные комментарии , которые размещаются между несколькими командами в строке или в идеале даже внутри данной команды.
    Внутристрочные комментарии являются наиболее гибкой (однострочной) формой и могут по определению также использоваться как комментарии EOL.

    • ver & REM^. ^<comment^> & verпозволяет вставлять комментарии между командами (опять же, любезно предоставлено ответом Джеба ), но обратите внимание на то, как <и как >нужно было ^-escaped, потому что следующие символы. нельзя использовать как есть:< > | (тогда как unescaped &или &&или ||запустить следующую команду).

    • %= <comment> =%, Как описана в большом ответе dbenham в , является наиболее гибкой формой , потому что он может быть помещен внутри команды (среди аргументов) .
      Он использует преимущества синтаксиса раскрытия переменных таким образом, чтобы выражение всегда расширялось до пустой строки - если текст комментария не содержит ни « %Мне:
      нравится» REM, ни « Мне нравится» , он %= <comment> =%хорошо работает как внутри (...), так и внутри блоков, но он более визуально различим; Единственным недостатком является то, что его сложнее набрать, легче ошибиться синтаксически и малоизвестно, что может затруднить понимание исходного кода, использующего эту технику.


Многострочные (целочисленные блоки) комментарии :

  • Ответ Джеймса К. показывает, как использовать gotoоператор и метку для разграничения многострочного комментария произвольной длины и содержания (который он использует для хранения информации об использовании).

  • Ответ Зи показывает, как использовать «нулевую метку» для создания многострочного комментария, хотя необходимо соблюдать осторожность, чтобы завершить все внутренние строки ^.

  • В блоге Роба ван дер Вуде упоминается еще одна неясная опция, позволяющая завершить файл произвольным числом строк комментариев : открытие (только приводит к тому, что все, что следует после, игнорируется , если оно не содержит (не ^-эскэпед) ), т. е. до тех пор, пока блок не закрыт .


[1] Использование SET "foo=bar"для определения переменных, т. Е. Заключения двойных кавычек вокруг имени =и объединенного значения , необходимо в таких командах, как SET "foo=bar" & REM Set foo to bar., например , для обеспечения того, что следует за предполагаемым значением переменной (в данном случае до следующей команды) единое пространство) не случайно становится его частью.
(В качестве отступления: SET foo="bar"не только не избежать проблемы, но и сделать двойные кавычки частью значения ).
Обратите внимание, что эта проблема присуща SETи даже относится к случайным конечным пробелам после значения, поэтому рекомендуется всегда использовать SET "foo=bar"подход.


7

На этой странице сообщается, что использование «::» будет быстрее при определенных ограничениях. Просто нужно учитывать при выборе


2
Это правда, по крайней мере для Win7SP1, ::может быть в 6 раз быстрее, чемREM
jeb

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

Джеймс К., извините, я ошибался в значительной части того, что я сказал. Тест, который я сделал, был следующим:

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

0

Есть несколько способов комментировать в командном файле

1) Использование rem

Это официальный путь. По-видимому, выполнение занимает больше времени, чем ::, хотя, по-видимому, он прекращает синтаксический анализ раньше, чем обрабатываются символы. Процентное расширение происходит перед rem и ::идентифицируется, поэтому неправильное использование процентов, т.е.%~ приведет к ошибкам, если проценты присутствуют. Безопасно для использования в любом месте блоков кода.

2) Используя ярлыки :, ::или:; т.п.

Ибо :: comment': comment' является недопустимым именем метки, потому что оно начинается с недопустимого символа. Можно использовать двоеточие в середине метки. Если пробел начинается в начале метки, он удаляется : labelстановится :label. Если в середине метки появляется пробел или двоеточие, остальная часть имени не интерпретируется, что означает, что если есть две метки, :f:ooи :f rrобе будут интерпретироваться как, :fи будет переходиться только к более поздней определенной метке в файле. Остальная часть ярлыка фактически является комментарием. Есть несколько альтернатив ::, перечисленных здесь . Вы никогда не можете gotoили ярлык. и не будет работать.call::foogoto :foogoto ::foo

Они отлично работают вне блоков кода, но после метки в блоке кода, недействительной или нет, должна быть действительная командная строка. :: commentдействительно еще одна действительная команда. Это интерпретирует это как команду, а не метку; команда имеет приоритет. Это команда для записи на ::том, которая будет работать, если вы ее выполнили 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> nulили subst :: 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.