I recommend to first read How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Windows command processor parses an entire code block starting with (
and ending with the matching )
before executing the command making use of the command block. During processing of a command block all environment variable references in the form %variable%
are replaced by the current value of the environment variables or removed on an environment variable not existing at all. So the command block finally executed does not contain anymore any %variable%
. That behavior is very good explained at Variables are not behaving as expected.
So the code
set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
echo %prefix%
)
results in executing FOR with
echo C:\Program Files (x86)\MyProg
The closing parenthesis )
is not inside an argument string being enclosed in "
. For that reason the Windows command processor interprets it as end of the command block of command FOR.
It is syntactically not correct to specify one more command on same line after )
being interpreted as end of a command block without usage of an operator like &
or &&
or ||
. For that reason \MyProg
is interpreted as syntactically invalid command after )
marking end of the command block.
The help output by running cmd /?
in a command prompt window explains at end of last page that a file name containing a space or one of these characters &()[]{}^=;!'+,`~
must be enclosed in "
to get all these characters interpreted literally with exception of !
if delayed environment variable expansion is enabled, too.
The usage of "
around an argument string is not only required for file names, but for any argument string containing a space or one of these characters &()[]{}^=;!'+,`~
or the redirection operators <|>
which should be also interpreted literally by cmd.exe
.
So one solution would be:
set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
echo "%prefix%"
)
ECHO outputs the prefix with both "
, but it is good practice to output file/folder names with ECHO always enclosed in double quotes.
Another solution is escaping )
with ^
to be interpreted as literal character. In this case it is important that the line with echo
still has ^)
after replacing %prefix%
by the string value assigned to the environment variable prefix
. For that reason it is necessary to define the prefix
string with caret character interpreted as literal character which means two ^
are necessary left to )
on environment variable definition.
set prefix=C:\Program Files (x86^^)\MyProg
for %%x in (%*) do (
echo %prefix%
)
The environment variable prefix
is defined now with the string value:
C:\Program Files (x86^)\MyProg
This results in the FOR command block in getting )
interpreted as literal character.
It is also possible to define environment variable prefix
with being enclosed in double quotes to get caret character ^
interpreted as literal character which avoids the need to double this character being otherwise interpreted as escape character.
set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
echo %prefix%
)
One more solution is using delayed environment variable expansion:
@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
echo !prefix!
)
endlocal
But there is a problem with this solution caused by double parsing of the command lines because of enabled delayed expansion as it can be seen on modifying the code to
@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
echo !prefix!\%%~x
)
endlocal
and executing the batch file with the three arguments Hello!
and Test
and Start!
. The exclamation marks are no longer interpreted as literal characters because of enabled delayed expansion. For that reason the output is:
C:\Program Files (x86)\MyProg\Hello
But the output should be:
C:\Program Files (x86)\MyProg\Hello!
C:\Program Files (x86)\MyProg\Test
C:\Program Files (x86)\MyProg\Start!
This output can be get with:
set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
echo %prefix%\%%~x
)
However, really safe is printing the concatenated strings enclosed in double quotes.
set "prefix=%ProgramFiles(x86)%\MyProg"
for %%I in (%*) do (
echo "%prefix%\%%~I"
)
The output is in this case for the above example with the three arguments Hello!
and Test
and Start!
:
"C:\Program Files (x86)\MyProg\Hello!"
"C:\Program Files (x86)\MyProg\Test"
"C:\Program Files (x86)\MyProg\Start!"
See also my answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? It explains why the environment variable prefix
is defined with using "
left to variable name and at end of the string value assigned to the environment variable. That is the recommended syntax on definition of an environment variable.
Further, it is good practice not using as loop variable a case-sensitive interpreted letter for which a case-insensitive interpreted modifier exists as otherwise the results can be unexpected, especially on variable strings being concatenated dynamically.
Example:
@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
The output of this small batch file is:
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.
The intention is here to get output the number with x
appended and not to reference the not existing file extension of the three strings in set of command FOR.
Therefore the modifier letters ADFNPSTXZadfnpstxz
should not be used as loop variable although it is possible in most cases.