Я рекомендую сначала прочитать Как Windows Командный интерпретатор (CMD.EXE) анализирует сценарии?
Windows Командный процессор анализирует весь блок кода, начиная с (
и заканчивая соответствием )
перед выполнением команды, использующей командный блок. Во время обработки командного блока все ссылки на переменные среды в форме %variable%
заменяются текущим значением переменных среды или удаляются для переменной среды, которая вообще не существует. Таким образом, окончательно выполненный командный блок больше не содержит %variable%
. Это поведение очень хорошо объяснено на Переменные работают не так, как ожидалось .
Так что код
set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
echo %prefix%
)
приводит к выполнению FOR с помощью
echo C:\Program Files (x86)\MyProg
Закрывающая скобка )
не находится внутри строки аргумента, заключенной в "
. По этой причине командный процессор Windows интерпретирует его как конец командного блока команды FOR .
Синтаксически неправильно указывать еще одну команду в той же строке после )
интерпретируется как конец командного блока без использования оператора, такого как &
или &&
или ||
. По этой причине \MyProg
интерпретируется как синтаксически недопустимая команда после )
маркировки конца командного блока.
Вывод справки при запуске cmd /?
в окне командной строки в конце последней страницы объясняет, что имя файла, содержащее пробел или один из этих символов &()[]{}^=;!'+,`~
должно быть заключено в "
, чтобы все эти символы интерпретировались буквально, за исключением !
, если включено также замедленное расширение переменной среды.
использование "
вокруг строки аргумента требуется не только для имен файлов, но и для любой строки аргумента, содержащей пробел или один из этих символов &()[]{}^=;!'+,`~
или операторов перенаправления <|>
, которые также должны интерпретироваться буквально cmd.exe
.
Таким образом, одним из решений будет:
set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
echo "%prefix%"
)
ECHO выводит префикс с обоими "
, но рекомендуется выводить имена файлов / папок с ECHO всегда заключено в двойные кавычки.
Другим решением является экранирование )
с ^
в b Он интерпретируется как буквальный характер. В этом случае важно, чтобы строка с echo
все еще имела ^)
после замены %prefix%
на строковое значение, присвоенное переменной среды prefix
. По этой причине необходимо определить строку prefix
с символом каретки, интерпретируемым как буквальный символ, что означает, что два ^
необходимо оставить равными )
при определении переменной среды.
set prefix=C:\Program Files (x86^^)\MyProg
for %%x in (%*) do (
echo %prefix%
)
Переменная среды prefix
теперь определяется строковым значением:
C:\Program Files (x86^)\MyProg
В результате командный блок FOR приводит к интерпретации )
как буквенного символа.
It Также возможно определить переменную окружения prefix
, заключив ее в двойные кавычки, чтобы получить символ вставки ^
, интерпретируемый как литеральный символ, что устраняет необходимость удваивать этот символ, иначе интерпретируемый как escape-символ.
set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
echo %prefix%
)
Еще одно решение заключается в использовании отложенного расширения переменной среды:
@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
echo !prefix!
)
endlocal
Но существует проблема с этим решением, вызванная двойным анализом командных строк из-за включенного отложенного расширения, которое можно увидеть при изменении кода на
@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
echo !prefix!\%%~x
)
endlocal
и выполнение командного файла w Три аргумента Hello!
и Test
и Start!
. Восклицательные знаки больше не интерпретируются как буквенные символы из-за включенного расширения с задержкой. По этой причине вывод:
C:\Program Files (x86)\MyProg\Hello
Но вывод должен быть:
C:\Program Files (x86)\MyProg\Hello!
C:\Program Files (x86)\MyProg\Test
C:\Program Files (x86)\MyProg\Start!
Этот вывод можно получить с помощью:
set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
echo %prefix%\%%~x
)
Однако на самом деле безопасным является печать объединенных строк, заключенных в двойные кавычки.
set "prefix=%ProgramFiles(x86)%\MyProg"
for %%I in (%*) do (
echo "%prefix%\%%~I"
)
В этом случае вывод выводится для приведенного выше примера с тремя аргументами Hello!
и Test
и Start!
:
"C:\Program Files (x86)\MyProg\Hello!"
"C:\Program Files (x86)\MyProg\Test"
"C:\Program Files (x86)\MyProg\Start!"
См. Также мой ответ по Почему нет строкового вывода с 'echo% var%' после использования 'set var = text' в командной строке? Это объясняет, почему переменная окружения prefix
определяется с помощью используя "
слева от имени переменной и в конце строкового значения, назначенного переменной среды. Это рекомендуемый синтаксис для определения переменной среды.
Кроме того, рекомендуется не использовать в качестве переменной l oop a чувствительную к регистру интерпретируемую букву, для которой учитывается регистр интерпретируемый модификатор, так как в противном случае результаты могут быть неожиданными, особенно при динамическом объединении переменных строк.
Пример:
@echo off
cls
echo Loop with %%~Xx:
for %%x in ("1" 2 3) do echo %%~Xx run.
echo Loop with %%~xX:
for %%X in ("1" 2 3) do echo %%~xX run.
echo Loop with %%~Ix:
for %%I in ("1" 2 3) do echo %%~Ix run.
echo Loop with %%~#x:
for %%# in ("1" 2 3) do echo %%~#x run.
pause
Вывод этого небольшого пакетного файла: :
Loop with %~Xx:
run.
run.
run.
Loop with %~xX:
run.
run.
run.
Loop with %~Ix:
1x run.
2x run.
3x run.
Loop with %~#x:
1x run.
2x run.
3x run.
Цель состоит в том, чтобы получить выходной номер с добавлением x
и не ссылаться на несуществующее расширение файла трех строк в наборе команды FOR .
Поэтому буквы-модификаторы ADFNPSTXZadfnpstxz
не следует использовать в качестве переменной l oop, хотя это возможно в большинстве случаев.