Why do my .bat always loses its variable after the lengthy commands - file

Here is my .bat suffering from the adversity. Lets name it b.bat.
set loopnum=%1
set url=%2
del "%TEMP%\selectortemp.txt"
del "%TEMP%\selectortemp2.txt"
for /r %loopnum% %%i in (\*.*) do echo %%~ni%%~xi>>"%TEMP%\selectortemp.txt"
echo %loopnum%
pause
set count=0
for /f "usebackq delims=" %%a in (%TEMP%\selectortemp.txt) do set /a count+=1
set /a count2=1
:looping
for /f "tokens=1,2 delims==" %%a in (%config%) do (if %%a==url set url=%%b)
set /p firstline=<"%TEMP%\selectortemp.txt"
del "%url%\%firstline%"
echo "%firstline%"
pause
for /f "skip=1 tokens=*" %%A in (%TEMP%\selectortemp.txt) do echo %%A>>"%TEMP%\selectortemp2.txt"
del "%TEMP%\selectortemp.txt"
rename "%TEMP%\selectortemp2.txt" "selectortemp.txt"
if %count2%==%count% goto endlooping
set /a count2+=1
goto looping
:endlooping
At first, I call it by this:
for /l %%i in (0,1,3) do (call b.bat %%i C:\testing)

You are not very specific with your question.
I asume that you mean that your variables lose their content in the for loops.
You need to write !a! instead of %a%. But you can't do this with the variable used in the for loop. !!a is not possible.
Be sure to enable delayed expansion.
Here's some information about enabledelayedexpansion: SS64

Related

Why is my if statement within a function causing an error in batch?

I am coding a batch install script and I am trying to have this function run only if a certain variable is equal to 1. Whenever call this funtion I a normal syntax command("The syntax of the command is incorrect") followed by on the next line
"C:\Windows\System32> break>"
The code is below:
:updateStatus
IF %uploadInfo% EQU 1 (
REM finds IP
set ip_address_string="IP Address"
set ip_address_string="IPv4 Address"
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:%ip_address_string%`) do (
SET ip=%%f
REM goto :eof
)
REM Removes spaces from IP
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "tokens=* delims= " %%a in ("%ip%") do set ip=%%a
for /l %%a in (1,1,100) do if "!ip:~-1!"==" " set ip=!ip:~0,-1!
REM echo IP Adress: %ip%
SET txtLoc=C:\CadVersionInfo\%ip%.txt
SETLOCAL DISABLEDELAYEDEXPANSION
REM SETs time
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%a/%%b/%%c)
For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set mytime=%%a:%%b)
REM writes info
if not exist C:\CadVersionInfo\ (mkdir C:\CadVersionInfo\)
if exist "%txtLoc%" (
del "%txtLoc%"
)
break>%txtLoc%
#echo %trunk%.%build% >>"%txtLoc%"
#echo %TA%>>"%txtLoc%"
#echo %mdbname%>>"%txtLoc%"
REM ~1 is status
#echo %~1>>"%txtLoc%"
#echo %mytime% %mydate%>>"%txtLoc%"
#echo %notes%>>"%txtLoc%"
REM Sets location on server to store data
SET txtLocRemote=\\cd-ptt\CAD Downloads\Webserver\servers\*
xcopy %txtLoc% "%txtLocRemote%" /q /y
)
goto:eof
Rather than trying to deal with all of your variables using delayedexpansion, it's probably simpler to reverse your if statement and just use a goto, like this:
:updateStatus
IF NOT %uploadInfo% EQU 1 goto end
{do stuff}
:end
Another solution is set the variables outside the loop regardless, and only process the loop if it is needed.
I have edited this post many times and in the end used the solution from Wes, adding this here just to illustrate some different ways to achieve parts of your code.
The spaces in the IP variable are removed in a simpler way.
The redirection to the file is changed - it can avoid trailing spaces using this method.
The * in SET "txtLocRemote=\\cd-ptt\CAD Downloads\Webserver\servers\*" is not legitimate syntax for xcopy and will need to be modified.
In this code block the two lines were replaced and the 2>nul simply hides irrelevant text from the console. It will always attempt to create the folder and delete the file, and no damage will be done if the folder already exists or if the file doesn't exist.
if not exist C:\CadVersionInfo\ (mkdir C:\CadVersionInfo\)
break>"%txtLoc%"
Here's your edited code:
:updateStatus
IF %uploadInfo% NEQ 1 goto :EOF
REM finds IP
set ip_address_string="IP Address"
set ip_address_string="IPv4 Address"
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:%ip_address_string%`) do (
SET ip=%%f
REM goto :eof
)
REM Removes spaces from IP
SET "ip=%ip: =%"
REM echo IP Adress: %ip%
SET "txtLoc=C:\CadVersionInfo\%ip%.txt"
REM Sets location on server to store data
SET "txtLocRemote=\\cd-ptt\CAD Downloads\Webserver\servers\*"
REM SETs time
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%a/%%b/%%c)
For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set mytime=%%a:%%b)
REM writes info
mkdir "C:\CadVersionInfo" 2>nul
del "%txtLoc%" 2>nul
>>"%txtLoc%" #echo %trunk%.%build%
>>"%txtLoc%" #echo %TA%
>>"%txtLoc%" #echo %mdbname%
REM ~1 is status
>>"%txtLoc%" #echo %~1
>>"%txtLoc%" #echo %mytime% %mydate%
>>"%txtLoc%" #echo %notes%
xcopy "%txtLoc%" "%txtLocRemote%" /q /y
goto:eof

Batch Programming - I want to use a dynamic variable whose value will vary during the run time?

I have a requirement where I need to increment the variable and store a value into that. For example: Suppose Initially variable Batch1 has value 1000. Now i need to dynamically create subsequent batch variables and store incremental values into those variables e.g. next dynamically created variable should be Batch2 and value it hold should be 1001. Similarly Batch3=1003, Batch4=1004 and so on...
Is this possible in Batch scripting?
You could do this with a method:
#echo off
Rem Start Code
goto :start
:Update_Method
if "%1"=="" goto :eof
if "%2"=="" goto :eof
if "%3"=="" goto :eof
setlocal enabledelayedexpansion
set base=%1
set /a start=%2
set /a end=%3
for /l %%a in (1,1,%end%) do (set /a !base!%%a=!start!+%%a-1)
set base=
set start=
set end=
goto :eof
:start
Rem Start Rest of Code
And that should do what you want. Simply use as:
call Update_Method Batch 1000 10
And that will create:
Batch1=1000
Batch2=1001
Batch3=1002
...
Batch10=1009
Call it whenever you want to "dynamically" change the values.
I still have to test this, but it doesn't seem faulty.
Mona
#echo off
setlocal
for /l %%a in (1 2 50) do call :incrementalSet batch %%a
set batch
endlocal
exit /b
:incrementalSet basename value
setlocal enableextensions enabledelayedexpansion
set "last=0"
:: if the name of the variable can collide with something in the environment,
:: the following line should be used. If not, it is an unnecessary overhead
:: for /f "tokens=1 delims==" %%y in ('set %~1 2^>nul^|findstr /r /b /c:"%~1[0-9][0-9]*="') do (
:: if the base name does not contain numbers, the loop can be reduced to
:: for /f "tokens=1 delims=%~1=" %%y in ('set %~1 2^>nul') do if %%y gtr !last! set "last=%%y"
for /f "tokens=1 delims==" %%y in ('set %~1 2^>nul') do (
set "test=%%y"
set "test=!test:*%~1=!"
if defined test if !test! gtr !last! set "last=!test!"
)
set /a "last+=1"
endlocal & set "%~1%last%=%~2" & exit /b

Loop through list, set variable

I'm trying to make a loop that goes through a file with filenames on each line, set the first filename as a variable and execute the rest if the script. Then take the second line and do the same.
etc. etc.
The problem is that it only does the first line of filenames.txt
#echo off
for /F "tokens=*" %%G in (filenames.txt) do (
set filename=%%G
script
script
script
)
pause
It has be a batch file.
The whole script:
#ECHO OFF
for /F "tokens=*" %%G in (filenames.txt) do (
SET FileName=%%G
SET Word1="ts_confirmImplicitSAMM.gram"
SET Word2="SWIrcnd"
for /f "tokens=3" %%f in ('find /c /i %Word1% %FileName%') do set PairsToShow=%%f
SET /a Lines1=0, Lines2=0
FOR /f "delims=" %%a IN ('findstr "%Word1%" "%FileName%"') DO (
SET "str=%%a"
SET /a Lines1+=1
SETLOCAL enabledelayedexpansion
SET "$1!Lines1!=!str!"
FOR /f "tokens=1*delims==" %%b IN ('set "$1"') DO (IF "!"=="" endlocal)&SET "%%b=%%c"
)
FOR /f "delims=" %%a IN ('findstr "%Word2%" "%FileName%"') DO (
SET "str=%%a"
SET /a Lines2+=1
SETLOCAL enabledelayedexpansion
SET "$2!Lines2!=!str!"
FOR /f "tokens=1*delims==" %%b IN ('set "$2"') DO (IF "!"=="" endlocal)&SET "%%b=%%c"
)
SET /a Lines=Lines1+Lines2
ECHO(%Lines% lines read from %FileName%.
IF %Lines1% leq %Lines2% (SET /a MaxPairs=Lines1) ELSE SET /a MaxPairs=Lines2
IF %PairsToShow% gtr %MaxPairs% (
ECHO only text for %MaxPairs% pairs NOT %PairsToShow% :/
GOTO :END
)
(FOR /l %%a IN (1,1,%PairsToShow%) DO (
SETLOCAL ENABLEDELAYEDEXPANSION
CALL SET "Line1=%%$1%%a%%"
CALL SET "Line2=%%$2%%a%%"
<NUL SET /p "=!Line1!"
ECHO !Line2!
ENDLOCAL
))>> result1.txt
ENDLOCAL
TYPE result1.txt| FINDSTR /V EVNT=SWIgrld >> result.txt
DEL result1.txt
PAUSE
)
Without seeing the rest of your script... you probably need to do 1 of 2 things:
Use SETLOCAL ENABLEDELAYEDEXPANSION (as 2nd line of your script) and then reference the variable filename as !filename! instead of %filename% to use the run-time value instead of the load-time value. But that could cause other problems, depending on what goes on in "script".
Just use %%G instead of filename

How to set a variable inside a loop for /F

I made this code
dir /B /S %RepToRead% > %FileName%
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
echo %%a is working fine but echo %z% returns "echo disabled".
I need to set a %z% because I want to split the variable like %z:~7%
Any ideas?
There are two methods to setting and using variables within for loops and parentheses scope.
setlocal enabledelayedexpansion see setlocal /? for help. This only works on XP/2000 or newer versions of Windows.
then use !variable! instead of %variable% inside the loop...
Create a batch function using batch goto labels :Label.
Example:
for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a
goto End
:Foo
set z=%1
echo %z%
echo %1
goto :eof
:End
Batch functions are very useful mechanism.
You probably want SETLOCAL ENABLEDELAYEDEXPANSION. See https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993 for details.
Basically: Normal %variables% are expanded right aftercmd.exe reads the command. In your case the "command" is the whole
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
loop. At that point z has no value yet, so echo %z% turns into echo. Then the loop is executed and z is set, but its value isn't used anymore.
SETLOCAL ENABLEDELAYEDEXPANSION enables an additional syntax, !variable!. This also expands variables but it only does so right before each (sub-)command is executed.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
This gives you the current value of z each time the echo runs.
I struggeld for many hours on this.
This is my loop to register command line vars.
Example : Register.bat /param1:value1 /param2:value2
What is does, is loop all the commandline params,
and that set the variable with the proper name to the value.
After that, you can just use
set value=!param1!
set value2=!param2!
regardless the sequence the params are given. (so called named parameters).
Note the !<>!, instead of the %<>%.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%P IN (%*) DO (
call :processParam %%P
)
goto:End
:processParam [%1 - param]
#echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
#echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
#echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof
:End
Simple example of batch code using %var%, !var!, and %%.
In this example code, focus here is that we want to capture a start time using the built in variable TIME (using time because it always changes automatically):
Code:
#echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="
for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%
:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0
Comments on code:
Use enabledelayedexpansion
The first three SET lines are typical
uses of the SET command, use this most of the time.
The next line is a for loop, must use %%A for iteration, then %%B if a loop inside it
etc.. You can not use long variable names.
To access a changed variable such as the time variable, you must use !! or set with !! (have enableddelayexpansion enabled).
When looping in for loop each iteration is accessed as the %%A variable.
The code in the for loop is point out the various ways to set a variable. Looking at 'SET OTHER=!START!', if you were to change to SET OTHER=%START% you will see why !! is needed. (hint: you will see NOTHING) output.
In short !! is more likely needed inside of loops, %var% in general, %% always a for loop.
Further reading
Use the following links to determine why in more detail:
Difference between %variable% and !variable! in batch file
Variable usage in batch file
To expand on the answer I came here to get a better understanding so I wrote this that can explain it and helped me too.
It has the setlocal DisableDelayedExpansion in there so you can locally set this as you wish between the setlocal EnableDelayedExpansion and it.
#echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z! Echoing the assigned variable in setlocal scope.
echo %%A Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z! &rem !z! Neither of these now work, which makes sense.
echo %z% &rem ECHO is off. Neither of these now work, which makes sense.
echo %%A Echoing the variable in its local scope, will always work.
)
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=
for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)
rem: backup will be 2019
echo %backup%
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
You can use a macro if you access a variable outside the scope
#echo off
::Define macro
set "sset=set"
for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)
echo x1=%x[1]% x2=%x[2]% x3=%x[3]% x4=%x[4]% y=%y%
:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion
echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]! "
if %%a equ 4 echo y=%y%
)
pause
I know this isn't what's asked but I benefited from this method, when trying to set a variable within a "loop". Uses an array. Alternative implementation option.
SETLOCAL ENABLEDELAYEDEXPANSION
...
set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3
set "i=0"
:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!
echo CurrentService: !SERVICE!
set /a "i+=1"
GOTO :ServicesLoop
)
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)

MS-DOS batch script: assignment operation

I am referring to below threat Batch files: How to read a file?. For retrieving the line by line from a text file. I am using the below script:
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ paths.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
echo !var!
ENDLOCAL
)
Code is working fine! The values in !var! can not assign to any variable. :( I struct there, Please anyone help to read line by line and I wish to assign to some variable and want to manipulate that variable. Please help to solve this situation.
Update:
#ECHO off
CLS
SET PROJ_DIR=D:\workspace\proj
SET PROMO_DIR=D:\TEST
SET SOURCE_CODE=\Source Code
SETLOCAL DisableDelayedExpansion
for /f %%a in (paths.txt) do (
SET "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
set FILE_PATH=!var://www.domain.com/path/dir=!
SET FILE_PATH=!FILE_PATH:/=\!
SET PROMO_FILE_PATH=!PROMO_DIR!!SOURCE_CODE!!FILE_PATH!
FOR %%i IN ("!PROMO_FILE_PATH!") DO SET FOLDER_PATH=%%~dpi
FOR %%i IN ("!PROMO_FILE_PATH!") DO SET FILE_NAME=%%~nxi
IF EXIST "!FOLDER_PATH!" GOTO F3
MKDIR "!FOLDER_PATH!"
:F3
IF NOT EXIST "!PROJ_DIR!!FILE_PATH!" GOTO F4
COPY "!PROJ_DIR!!FILE_PATH!" "!FOLDER_PATH!"
:F4
ECHO Cannot find the file under "!PROJ_DIR!!FILE_PATH!"
ENDLOCAL
)
SET CLOSE_CONFIRM=
SET /P CLOSE_CONFIRM=Press any key to close the window...%=%
paths.txt
//www.domain.com/path/dir/dir1/dir2/file1.txt
//www.domain.com/path/dir/dir1/dir2/file2.txt
//www.domain.com/path/dir/dir1/dir2/file3.txt
//www.domain.com/path/dir/dir1/dir2/file4.txt
//www.domain.com/path/dir/dir1/dir3/file1.txt
Command Output
1 file(s) copied.
Cannot find the file under "D:\workspace\proj\dir1\dir2\file1.txt"
1 file(s) copied.
Cannot find the file under "D:\workspace\proj\dir1\dir2\file2.txt"
Press any key to close the window...
thanks..
The key is the delayed expansion, expand your variables inside of parenthesis always with ! not with %.
A sample that changes X with Y
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ paths.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
set "myVar=!var!"
set "myVar=!myVar:X=Y!"
echo X replaced with Y =!myVar!
ENDLOCAL
)
In your updated version the goto :label stops the for-loop immediatly
Better rewrite it to IF-Blocks
IF NOT EXIST "!FOLDER_PATH!" (
MKDIR "!FOLDER_PATH!"
)
IF EXIST "!PROJ_DIR!!FILE_PATH!" (
COPY "!PROJ_DIR!!FILE_PATH!" "!FOLDER_PATH!"
) ELSE
(
ECHO Cannot find the file under "!PROJ_DIR!!FILE_PATH!"
)
The other answers here cover the tricky bits of delayed expansion in this code. I want to add that you can often avoid most delayed expansion problems by rolling the parentheses out into a subroutine.
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ paths.txt"`) do call :HandlePath %%a
goto :eof
================
:HandlePath
set "var=%*"
set "var=%var:*:=%"
echo %var%
goto :eof
I find this code easier to maintain because each line is parsed an executed exactly when you would expect.
If you want to read each line and manipulate it:
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=*" %%a IN (paths.txt) DO (
set var=%%a
ECHO %var%
PAUSE
)
ENDLOCAL
If you are trying to search a string from a file and manipulate it:
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=* usebackq" %%a IN (`FIND /I 'string to search for' "C:\folder\paths.txt"`) DO (
set var=%%a
ECHO %var%
PAUSE
)
ENDLOCAL
If you are trying to search a string and manipulate each word in the string:
SETLOCAL EnableDelayedExpansion
FOR /F "usebackq tokens=1-999 delims= " %%a IN (`FIND /I 'string to search for' "C:\folder\paths.txt"`) DO (
REM %%a = first word %%b = second word etc. through the alphabet
set var1=%%a%%b%%c
set var2=%%d
ser var3=%%e
ECHO %var1% %var2% %var3%
PAUSE
)
ENDLOCAL
This works for me:
#echo off
SETLOCAL EnableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ paths.txt"`) do (
set var=%%a
set var=!var:*:=!
echo !var!
)
ENDLOCAL

Resources