Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to escape percent (%) when using parameters in a FOR loop?

Tags:

batch-file

The usual escape character in dos batch files is caret, ^. However for percent, %, the delimiter for variables, the escape is to double up the percents: %%cd%%. Things change when using parameter extensions inside a for loop. Instead of %%~dpnx0 emitting %~dpnx0, as it will outside the loop, it carries out the substitution, emitting D:\Scripts\foo.py.

Here's a batch file demonstration:

@echo off
echo This is a pipe: ^|
echo Use this var for the current directory: %%cd%%
echo Use this to echo full path of running batch file: %%~dpnx0

for %%a in (foo.py baz.py) do (
  echo @python %%~dpnxa ^> %%~na.bat
  )

These are the results I get:

This is a pipe: |
Use this var for the current directory: %cd%
Use this to echo full path of running batch file: %~dpnx0
@python d:\Scripts\foo.py > foo.bat
@python d:\Scripts\baz.py > baz.bat

But this is what I want:

This is a pipe: |
Use this var for the current directory: %cd%
Use this to echo full path of running batch file: %~dpnx0
@python %~dpnxa > foo.bat
@python %~dpnxa > baz.bat

I've tried doubling, tripling, and quadrupling the percents as well is interspersing carets throughout, all without success.

like image 973
matt wilkie Avatar asked Dec 04 '12 18:12

matt wilkie


2 Answers

It is impossible to prevent a FOR variable expression from expanding. If a FOR is in effect with variable X defined, then the FOR expansion phase will always expand %X.

But you can hide the percent behind another FOR variable :)

The following gives the result you are looking for:

@echo off
echo This is a pipe: ^|
echo Use this var for the current directory: %%cd%%
echo Use this to echo full path of running batch file: %%~dpnx0

for %%P in (%%) do for %%A in (foo.py baz.py) do (
  echo @python %%P~dpnxA ^> %%~nA.bat
)

FOR variables have global scope (though they are only accessible within the DO clause). This can lead to an insidious problem. Any time you have a subroutine that uses a percent literal within a FOR loop, then you are at risk of unintended results! A FOR statement issued before your CALL can influence the results of a FOR DO clause within your CALLed routine.

@echo off
for %%) in (Yikes!) do call :test
exit /b

:test
echo This works outside loop (confidence = 100%%)
for %%A in (1) do echo This does not work inside loop (confidence = 100%%)
for %%P in (%%) do for %%A in (1) do echo This works inside loop (confidence = 100%%P)
exit /b

Here is the output

This works outside loop (confidence = 100%)
This does not work inside loop (confidence = 100Yikes
This works inside loop (confidence = 100%)
like image 110
dbenham Avatar answered Sep 25 '22 04:09

dbenham


You could use delayed expansion or call-percent expansion, or like dbenham shows expanding another FOR variable.

setlocal EnableDelayedExpansion
set percent=%%
set "line=%%~dpnxa ^>"
for %%a in (foo.py baz.py) do (
  echo @python !percent!~dpnxa ^> %%~na.bat
  call echo @python %%line%% %%~nxa.bat
)
like image 28
jeb Avatar answered Sep 23 '22 04:09

jeb