That's because you enable delayed expansion after set "line=%%a"
:
set "line=%%a"
@echo on & setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set "line=!line:.wav=%NewExtension%!"
If you enable delayed expansion before assigning %%a
:
@echo on & setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set "line=%%a"
set "line=!line:.wav=%NewExtension%!"
you'll get
Special | < > ~ 23 { [ ] } € $ % & / ( ) = ? chars.flac
instead of
Special | < > ~ 23 { [ ] } ! " ′ ' ` ü?? @ ; : € $ % & / ( ) = ? chars.flac
Edit: Delayed expansion controls when variables in a statement are expanded. The critical statement in your script is the line
set "line=%%a"
which assigns the value of the loop variable %%a
to the variable line
. With delayed expansion disabled the literal value of %%a
is assigned, because the script interpreter cannot expand %%a
at parse time. However, when you enable delayed expansion, bang-variables are expanded at execution time, so the interpreter looks at the value of %%a
and expands any !whatever!
in it before the result is assigned to the variable line
.
Perhaps it'll become clearer with an example. If you add a line
%foo% !foo!
to the input file and define the variable in the script:
@echo on & setlocal ENABLEEXTENSIONS
set "foo=bar"
set "InFile=%~1"
...
When you enable delayed expansion after set "line=%%a"
neither %foo%
nor !foo!
are expanded before %%a
is assigned to the variable line
(the interpreter doesn't see the value of %%a
before execution time), so you get this output:
%foo% !foo!
When you enable delayed expansion before set "line=%%a"
the interpreter will expand bang-variables before assigning the result to the variable line
, so you get this output:
%foo% bar
%foo%
would only be expanded at parse time, at which point the interpreter cannot see the actual value of %%a
, so %foo%
remains a literal %foo%
here.
Further assignments like set "line=!line:.wav=%NewExtension%!"
don't affect bangs or percent signs inside a variable, because expansion isn't transitive, i.e. it translates !line!
to %foo% bar
(or %foo% !foo!
) and then stops.
You can force the expansion of (percent-)variables inside variables with the call
command, though. A command call set "line=!line!"
first expands to call set "line=%foo% bar"
in the current context, and then call
evaluates set "line=%foo% bar"
in a new context where %foo%
is expanded to bar
as well, so the variable line
is assigned the value bar bar
.
Just as a side note: your code is way too complicated. You'd get the exact same results with this:
set "FileToParse=%~1"
set "OutputFile=%~2"
set "NewExtension=%~3"
for /F "usebackq tokens=* delims=" %%a in ("%FileToParse%") DO (
set "line=%%a"
@setlocal ENABLEDELAYEDEXPANSION
set "line=!line:.wav=%NewExtension%!"
echo(!line!>>"%OutputFile%"
endlocal
)