I have 2 issues with my Batch file, I think they are due to EnableDelayedExpansion.
I am basing my script in this post from SO.
I need EnableDelayedExpansion for another part of my script, so I need to keep it.
This is my script:
#echo off
set myPath=Subfolder1
set folderList=input.txt
set originalPath=%~dp0
cd %myPath%
setlocal EnableDelayedExpansion
:process
for /F "tokens=*" %%S in (%~dp0\%folderList%) do (
echo Folder %%S
REM echo Folder %%S prints: Folder folderName
set testPath=C:\BatchTests\%%S\
echo test path: %testPath%
REM echo test path: %testPath% prints: test path:
)
echo %originalPath%
REM echo %originalPath% prints: C:\BatchTests\
cd %originalPath%
pause
testPath seems to be always empty, and the line cd %originalPath%.
What am I doing wrong? What is the right way to use/set testPath?
The second issue I am having is different, so I opened a separate question here.
To use delayed expansion in batch you need to parts.
First you have to enable it with setlocal EnableDelayedExpansion.
And then you can expand any variable with exclamation marks instead of percent signs.
setlocal EnableDelayedExpansion
set var=origin
(
set var=New Value
echo Percent: %var%
echo delayed: !var!
)
The output is
Percent: origin
delayed: New Value
Percent expansion is evaluated when a command or block is parsed.
Delayed expansion is evaluated when a command is executed.
Related
This part of my code is supposed to run by all the lines of a file, detecting the percent signs and changing to double percent signs. And then, echo the result.
Heres my code:
#echo off
REM | Using this line to test the code: % %% %%% %test%
setlocal enabledelayedexpansion
for /F "tokens=*" %%a in ( %~0 ) do set %a=!%a:%%=%%%%! & echo %%a
pause
And this is the output it is generating:
#echo off
REM | Using this line to test the code: % %% %%% %test%
setlocal enabledelayedexpansion
for /F "tokens=*" %%a in ( %~0 ) do set %a=%%=%%%% & echo %%a
pause
What is wrong with it? I can't seem to find any problems with the line!
find/replace does not work with FOR variables. It only works with environment variables. So you must transfer the %%a value to an environment variable, and then use the environment variable to do the find/replace.
The expansion is within a parenthesized block (part of the FOR loop) so you must use delayed expansion. FOR loops will corrupt values containing ! if delayed expansion is enabled when the FOR variable is expanded. So delayed expansion must be toggled on and off within the loop.
There is no need to change the variable and then echo the new value. Simply echo the changed value directly, without storing the new value in the variable.
#echo off
setlocal disableDelayedExpansion
REM | Using this line to test the code: % %% %%% %test%
for /f "usebackq delims=" %%a in ("%~f0") do (
set "str=%%a"
setlocal enableDelayedExpansion
echo !str:%%=%%%%!
endlocal
)
pause
I have following code snippet
setlocal enableextensions enabledelayedexpansion
set b=123
for %%f in (.\input\*.mgc) do (
set "b=%%~nf"
echo %b%
)
I am expecting it to output file name with no extension but I always get "123". I concluded it has something with late expansion but not quite sure where the problem comes from. I have also tried with echo !b! but in that situation it outputs only "!b!"
Try replacing %b% with !b! as explained here:
The variable whose expansion should be delayed should be surrounded by exclamation marks instead of percent signs.
setlocal enableextensions enabledelayedexpansion
set b=123
for %%f in (.\input\*.mgc) do (
set "b=%%~nf"
echo !b!
)
I believe in hard evidence. I can't see you execute the code or if you are obfuscating the code at all so here is me executing your code. It works just like I said.
C:\BatchFiles\SO>type testing.bat
#echo off
setlocal enableextensions enabledelayedexpansion
set b=123
for %%f in (.\input\*.mgc) do (
echo %%~nf
set "b=%%~nf"
echo !b!
)
pause
C:\BatchFiles\SO>dir /b .\input\*.mgc
testfile.mgc
C:\BatchFiles\SO>testing.bat
testfile
testfile
Press any key to continue . . .
C:\BatchFiles\SO>
On my desktop, there is a folder named "test". Inside this folder is two files, "file1.txt" and "file2.txt".
Take a look at this simple batch script:
#ECHO OFF
SET test="C:\Users\Tyler\Desktop\test"
ECHO %test%
FOR /R %test% %%F IN (*) DO (
ECHO %%F
)
As you might expect, it outputs the following:
"C:\Users\Tyler\Desktop\test"
C:\Users\Tyler\Desktop\test\file1.txt
C:\Users\Tyler\Desktop\test\file2.txt
Now take a look at this variation:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET test="C:\Users\Tyler\Desktop\test"
ECHO !test!
FOR /R !test! %%F IN (*) DO (
ECHO %%F
)
ENDLOCAL
I would expect its output to be no different. However, here it is:
"C:\Users\Tyler\Desktop\test"
It appears that !test! gets expanded in the ECHO !test! line, but not in the FOR /R !test! line, becoming just !test!. Since that is, of course, not a valid path, the FOR /R loop never iterates.
Why is this? What am I missing?
Why FOR works different than ECHO is because the batch parser (cmd.exe) has special parsing rules for FOR, IF and REM.
Therefore delayed expansion doesn't work for the parameters here, only for the arguments inside the parenthesis.
Only percent expansion works for the parameters, as the parser executes the percent expansion phase just before it switches to the special FOR parser rules.
If you can't use percent expansion, as you are inside of a block you can move the code to an own function and call it.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET test="C:\Users\Tyler\Desktop\test"
ECHO !test!
call :doMyLoop test
exit /b
:doMyLoop
set "arg=!%1!"
FOR /R %arg% %%F IN (*) DO (
ECHO %%F
)
I want to simply add 10 to the variable "%%i" in this batch file code and print it out to the screen. numbers.txt is a file that contains a single column of numbers.
FOR /F %%i IN (numbers.txt) DO (
set /a "T=%%i+10"
#echo %T%
)
For example, if %%i was 1 I would want T to be 11.
Thanks.
Without delayed expansion, you can use:
#echo off
set constant=10
FOR /F %%i IN (numbers.txt) DO (
set /a "T=%%i+%constant%"
call echo %%T%%
)
constant does not need delayed expansion, because it's constant throughout the loop.
Try the following code, it should work according to your description in the question :
#echo off
setlocal enabledelayedexpansion
set T=0
set K=10
for /f %%i in (numbers.txt) do (
set /A T=!K! + %%i
echo !T! )
endlocal
Save it as a .bat file extension and run from command prompt.
If file numbers.txt contain :
1
2
3
Output will be:
11
12
13
You can do this with delayed environment variable expansion. In the shell, you must first type
cmd /v
Then you can execute this batch script:
FOR /F %%i IN (numbers.txt) DO (
set /a T=%%i + 10
#echo !T!
)
If you don't run cmd /v first, then it will simply output !T! literally. See set /? for details on delayed environment variable expansion.
Start your batch file by turning on delayed expansion. With delayed expansion enabled, reference a variable inside a loop using ! instead of %.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F %%i IN (numbers.txt) DO (
set /a "T=%%i+10"
#echo !T!
)
I am trying to pass file names as FOR loop parameters to a separate batch file. The problem is, if a file name contains special characters (especially %), the parameter doesnt go to the called script. EG -
The FIRST_SCRIPT.bat is as follows -
cd "C:\theFolder"
for /R %%a in (*.*) do call SECOND_SCRIPT "%%~a"
The SECOND_SCRIPT.bat is as follows -
ECHO %1
If a file name contains % eg. "% of STATS.txt", the output ends up being
of STATS.txt
Which is wrong. I have tried using Setlocal DisableDelayedExpansion but with little success
Setlocal DisableDelayedExpansion
for /R %%a in (*.*) do (
SET "var=%%~a"
Setlocal EnableDelayedExpansion
call TEST_UPGRADE "%var%" "%%~a"
)
There are other stackoverflow answers, but they all need the % character to be known before hand. Since the file names are not in our control, these solutions won't work for us. Is there any way of handling this?
Thanks!
platform: cmd.exe for Windows XP
Aacini shows a solution that would work with % and also ! but it fails with carets ^.
But the solution is simple.
First it's necessary to disable the delayed expansion to handle the exclamation marks.
The filename is now exactly in the var variable.
The problems with carets and percents are caused by the CALL.
This can be solved with the CALL itself by use only the second percent expansion phase of the CALL by using %%var%%.
Setlocal DisableDelayedExpansion
for /R %%a in (*.*) do (
SET "var=%%~a"
call TEST_UPGRADE "%%var%%"
)
The next problem is in the second.bat to display the filename.
This should be done with delayed expansion enabled to avoid problems with special characters, or you need always quotes.
set "var=%~1"
setlocal EnableDelayedExpansion
echo Filename: !var!
solution with a temp file:
first.bat
#ECHO OFF &SETLOCAL
REM to escape the '%' use a second '%'
SET "var=40%% &off!.txt"
REM get a random temp file name
:loop
SET "tname=%temp%%random%%random%"
IF EXIST "%tname%" GOTO :loop
SETLOCAL ENABLEDELAYEDEXPANSION
REM save the variable in the file
>"%tname%" (ECHO(!var!)
CALL "second.bat" "%tname%"
ENDLOCAL
second.bat
#ECHO OFF &SETLOCAL
SET "tname=%~1"
<"%tname%" set/p"var="
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !var!
DEL "%tname%" /F /Q
..output is:
40% &off!.txt
EDIT: I added the enable/disable delayed expansion technique to avoid problems with exclamation-mark character.
#echo off
setlocal DisableDelayedExpansion
for /F "delims=" %%a in ('dir /B *.txt') do echo %%a
echo/
for %%a in (*.txt) do (
SET "var=%%a"
setlocal EnableDelayedExpansion
call :TEST_UPGRADE "!var:%%=%%%%!" "%%~a"
endlocal
)
goto :EOF
:TEST_UPGRADE
ECHO First: %1 Second: %2
exit /B
Output example:
% of STATS.txt
Discount of 10% in all.txt
Normal file.txt
First: "% of STATS.txt" Second: " of STATS.txt"
First: "Discount of 10% in all.txt" Second: "Discount of 10 in all.txt"
First: "Normal file.txt" Second: "Normal file.txt"