Batch FOR loop IF and SET conflict - batch-file

So, I was writing a small batch-file game, and came across this problem in the FOR loop.
This is a small chunk of my code.
If the player picks-up the rock, check if it is on the table, then put it in your pack.
The problem is the SET command doesn't work.
I guess that lines executes as : set %table1%=empty not : set table1=empty.
I feel like there is a simple solution to this problem, I've trying for a few days in my spare time, but haven't gotten it.
Any help is appreciated!
set take=rock
set table1=box
set table2=rock
set table3=wrench
for %%x in (%table1% %table2% %table3%) do (
if %%x==%take% (
set %%x=empty
set pack=%%x
goto tableRoom
)
)

The main problem with your code is set %%x=empty for a logical reason: the for loop is iterating over the list of the values inside the table variables, not over the name of the variables so you can not change the value of the variable when you don't have a reference to the variable, you have the value inside it.
Enabling delayed expansion and iterating over the names of the variables can solve the problem
#echo off
setlocal enableextensions enabledelayedexpansion
set "take=rock"
set "table1=box"
set "table2=rock"
set "table3=wrench"
for %%x in (table1 table2 table3) do (
if "!%%x!"=="%take%" (
set "%%x=empty"
set "pack=%take%"
goto tableRoom
)
)
:tableRoom
set table
exit /b
%%x will hold the name of the variable, so when !%%x! is executed it will be something like !table1!, retrieving the value inside the variable (when delayed expansion is enabled).
If the names of the used variables (table...) does not collide with anything else, and you don't want to use delayed expansion, it is possible to take the output of set table command (the list the variables with its values) and process this list with a for /f command, splitting the command output, separating variable name and value
#echo off
setlocal enableextensions disabledelayedexpansion
set "take=rock"
set "table1=box"
set "table2=rock"
set "table3=wrench"
for /f "tokens=1,* delims==" %%a in ('set table') do (
if "%%b"=="%take%" (
set "%%a=empty"
set "pack=%take%"
goto tableRoom
)
)
:tableRoom
set table
exit /b
The output of set command contains var=value. Each of the lines is splitted using the = as a delimiter to get two tokens (tokens=1,*). %%a will hold the first token (the variable name) and %%b the rest of the line (the value in the variable)

You can use a counter to now which table you're testing in your FOR loop and
set directly the table!N! as empty :
set take=rock
set table1=box
set table2=rock
set table3=wrench
setlocal enabledelayedexpansion
set $c=1
for %%x in (%table1% %table2% %table3%) do (
if %%x==%take% (
set table!$c!=empty
set pack=%%x
goto tableRoom
)
set /a $c+=1
)
:tableroom
echo table2 is now : %table2%

Related

How Do I Set A Variable To An Individual Value in A txt File Used For Data Storage?

As in the question I am having trouble finding a good answer on how to extract values from a txt file into multiple variables, my plan for this is to incorporate saving into the game I mentioned in my previous question(I already have the values saving into the file I just need to be able to receive them back)
#echo off
set User=DNABuster
set /a Round=1
for /f "tokens=* delims=" %%x in (Save%User%.txt) do (
set a=%%x %0
set b=%%x %1
set c=%%x %2
set d=%%x %3
)
Is my current code(%0 is just a placeholder until I figure out how to get the first value alone to be equivalent to that variable), I also tried:
set User=DNABuster
set /a Round=1
set test=0
for /f "tokens=* delims=" %%x in (Save%User%.txt) do (
set /a test=%test%+1
set a.%test%=%%x
)
This answer I saw above a suggested answer was perfect for my problem:
Reserve a character (say, #) to be used as the first character of all of the variables you want to record.
so...
set #points=35
set action=3
set #money=22
To save all of the # variables, use
set #>filename
which would save #money and #points, but not action
To reload use
for /f "delims=" %%a in (filename) do set "%%a"
which will reload all of the variables saved in the file - which are, of course, only #variables. credit to Magoo for the post(I just copy pasted it to here for ease of access for new users)

Batch: using upper for loop parameter in lower one

The command im trying to run is:
::%items% is defined elsewhere and is the amount of items per line in the file
FOR /F "usebackq tokens=1-%items% delims=," %%1 IN (`TYPE %TextFile%`) DO (
FOR /l %%a in (%items%,-1,1) do (
set /a "number=%%a"
echo !number!
:: This is the main command I believe im having issues with
set word!number!=%%!number!
echo !word1!
echo !word2!
echo !word3!
)
set /a "lineused%randomline%=1"
goto exitloop
)
:exitloop
pause
Now what I'm trying to do is set the variable called wordX where X is the number of the token. Edit: Basically, trying to use the %% variabla from the upper for loop which the lower one is running inside of.
I could type all the lines of
set word1=%%1
set word2=%%2
set word3=%%3
but that would defeat the purpose of the versatile system I'm trying to build.
Format of the text file (%TextFile%) would simply be, in this case:
line1i1,line1i2
line2i1,line2i2
But I need for it to work also on for example:
line1i1,line1i2,line1i3,line1i4
line2i1,line2i2,line2i3,line1i4
Interesting idea, but that cannot work because FOR variable expansion takes place before delayed expansion. You need a method to get an extra round of FOR variable expansion.
You can CALL a subroutine, and then use a dummy FOR loop to re-establish a FOR context. FOR variables are global in scope as long as you are in a FOR loop context. So your subroutine can access a FOR variable that was defined earlier.
...
...
FOR /F "tokens=1-%items% delims=," %%1 IN ('TYPE %TextFile%') DO (
FOR /l %%a in (%items%,-1,1) do call :set %%a
echo !word1!
echo !word2!
echo !word3!
)
...
...
exit /b
:set
for %%. in (.) do set "word%1=%%%1"
exit /b
The above works, but I don't like it because CALLs are expensive (slow). This is typically not a problem when you only have a few CALLs. But in this case the CALL is in a tight loop - one for every column times the number of rows in the file. Ouch!
If you really want to parametize your SET statements, and you want decent performance, then you can define a dynamic "macro". Simply store the needed commands in a variable, and then execute the content of the variable within your loop.
Also note that the above is limited to 9 items (10 if you start with 0 instead of 1). It is easy to extend the supported item count to 26 if you use letters, and a lookup string.
Finally, your dynamic FOR is within some parenthesized block. Presumably your ITEMS is defined outside the block, otherwise %items% could not be used in the FOR /F definition. The SET macro must be expanded using regular expansion, so it should be defined at the same time ITEMS is defined - outside the outer loop.
set /a items=3
::Define SET macro
set "v= ABCDEFGHIJKLMNOPQRSTUVWXYZ"
set "set="
for /l %%N in (1 1 %items%) do set "set=!set!&set "word%%N=%%!v:~%%N,1!""
set "set=!set:~1!"
FOR ... some loop ... DO (
...
...
FOR /F "tokens=1-%items% delims=," %%A IN ('TYPE %TextFile%') DO (
%set%
echo !word1!
echo !word2!
echo !word3!
)
...
...
)
If the ITEMS variable must be set within the outer loop, then you must CALL out of the loop to establish the inner FOR /F loop.
Note that you dont need the TYPE to get contents of file, the FOR command itself can iterate over file content
You could try something like this:
#echo off
setlocal enabledelayedexpansion
set "TextFile=textfile.txt"
set /a "lineNum=0"
set /a "i=0"
for /f "tokens=*" %%a in (%TextFile%) do (
set /a lineNum=!line_num! + 1
set "line=%%a"
for %%b in ("!line:,=" "!") do (
set /a "i=!i!+1"
set /a "wordNum=!lineNum! * !i!
set "word!wordNum!=%%b"
)
)
echo !word1!
echo !word2!
echo !word3!
Given a file with the contents:
aaa,bbb,ccc,ddd
eee,fff,ggg,hhh
Output will be:
"aaa"
"bbb"
... and so on
To remove the quotes, use %%~b at the inner FOR at set "word!wordNum! line.
Also note that you don't even need to define the number of items per line!
Hope it helps,
Cheers!

Batch Programming: read txt and keep variables with a subroutine

I am writing a bactch-file for the first time and I have a problem that I couldn't solve yet. (I'm not a programmer but just a mechanical engineer, so please forgive me for my clumsiness.. And my english: I'm not a native speaker...)
I want to write a batch file (readlist.bat) that amongst other things should read in a txt-file (Text.txt). This batch-file then should be able to be called in another batch file with "call readlist.bat".
What is clear to me is the following: I have to define variables within the readlist.bat not only locally if I want them to be stored after readlist.bat finishes. I tried to realize this in the attached files at least for some of the variables. (Of course the final goal is to keep all the variables read in from the txt-file.)
But I cant manage this.
The Text.txt only contains "test variables"
readlist.btat should read in all rows in columns (this works). But the variables need to be defined "not locally". Therefore for testing reasons I added the following commands:
endlocal
set "job1fld=%job1fld%"
set "job1dat=%job1dat%"
set "job1kyw=%job1kyw%"
set "job2fld=%job2fld%"
set "job2dat=%job2dat%"
set "job2kyw=%job2kyw%"
set "job3fld=%job3fld%"
set "job3dat=%job3dat%"
set "job3kyw=%job3kyw%"
set vartest1=test1
set vartest2=test2
set vartest3=test3
set vartest4=test4
set vartest5=test5
set vartest5=test6
set vartest6=test7
the second block seems to work. Or lets say: Those variables are handed over to the callreadlist. But those are only manually set test-variables...
The first block however doesn't work and I can't figureout why...
How do I manage that all the variables read in are kept after readlist.bat finishes? Because I am an absolute greenhorn when it comes to batch-files, I would be glad if you don't only give me tipps but codes ;-)
Thank you in advance.
Roy
CALLREADLIST.BAT:
#echo off
call readlist.bat
set
pause
READLIST.BAT:
REM =================================
REM =================================
REM READ TXT
#echo off
setlocal ENABLEDELAYEDEXPANSION
REM START: Read 1. Column (Ordner)
set ColNo=1
set cntr1=0
for /F "delims=; tokens=%ColNo%" %%A in (Text.txt) do (
set /a cntr1=!cntr1! + 1
set job!cntr1!fld=%%A
)
REM END: Read 1. Column (Ordner)
REM START: Read 2. Column (Ordner)
set ColNo=2
set cntr2=0
for /F "delims=; tokens=%ColNo%" %%A in (Text.txt) do (
SET /A cntr2=!cntr2! + 1
set job!cntr2!dat=%%A
)
REM END: Read 2. Column (Ordner)
REM START: Read 3. Column (Ordner)
set ColNo=3
set cntr3=0
for /F "delims=; tokens=%ColNo%" %%A in (Text.txt) do (
SET /A cntr3=!cntr3! + 1
set job!cntr3!kyw=%%A
)
REM END: Read 3. Column (Ordner)
endlocal
set "job1fld=%job1fld%"
set "job1dat=%job1dat%"
set "job1kyw=%job1kyw%"
set "job2fld=%job2fld%"
set "job2dat=%job2dat%"
set "job2kyw=%job2kyw%"
set "job3fld=%job3fld%"
set "job3dat=%job3dat%"
set "job3kyw=%job3kyw%"
set vartest1=test1
set vartest2=test2
set vartest3=test3
set vartest4=test4
set vartest5=test5
set vartest5=test6
set vartest6=test7
TXT-FILE:
Ordner1;Job1;Input1
Ordner2;Job2;Input2
Ordner3;Job3;Input3
Ordner4;Job4;Input4
Ordner5;Job5;Input5
Ordner6;Job6;Input6
Ordner7;Job7;Input7
Ordner8;Job8;Input8
Ordner9;Job9;Input9
Ordner10;Job10;Input10
Ordner11;Job11;Input11
Ordner12;Job12;Input12
Ordner13;Job13;Input13
Ordner14;Job14;Input14
Ordner15;Job15;Input15
Ordner16;Job16;Input16
Ordner17;Job17;Input17
Ordner18;Job18;Input18
Ordner19;Job19;Input19
Ordner20;Job20;Input20
Ordner21;Job21;Input21
Ordner22;Job22;Input22
Ordner23;Job23;Input23
Ordner24;Job24;Input24
Ordner25;Job25;Input25
Ordner26;Job26;Input26
Ordner27;Job27;Input27
Ordner28;Job28;Input28
Ordner29;Job29;Input29
Ordner30;Job30;Input30
Ordner31;Job31;Input31
Ordner32;Job32;Input32
Ordner33;Job33;Input33
Ordner34;Job34;Input34
Ordner35;Job35;Input35
Ordner36;Job36;Input36
Ordner37;Job37;Input37
Ordner38;Job38;Input38
Ordner39;Job39;Input39
nice stuff:
#echo off
for /f %%a in ('^< Text.txt find /c /v ""') do set /a lines=%%a
<Text.txt (
for /l %%a in (1 1 %lines%) do (
set "line="
set /p "line="
for /f "tokens=1-3delims=;" %%d in ('call echo %%line%%') do (
set "job%%afld=%%d"&set "job%%asat=%%e"&set "job%%akyw=%%f"
)
))
set "line="&set "lines="
set "job"
This is easy, the real challenge is to give each job-variable a unique number (here 1-117) without delayed expansion.
Try:
...
REM END: Read 3. Column (Ordner)
endlocal&(
set "job1fld=%job1fld%"
set "job1dat=%job1dat%"
set "job1kyw=%job1kyw%"
set "job2fld=%job2fld%"
set "job2dat=%job2dat%"
set "job2kyw=%job2kyw%"
set "job3fld=%job3fld%"
set "job3dat=%job3dat%"
set "job3kyw=%job3kyw%"
set vartest1=test1
set vartest2=test2
set vartest3=test3
set vartest4=test4
set vartest5=test5
set vartest5=test6
set vartest6=test7
)
Essentially, what should happen is that the entire last line is parsed from the ENDLOCAL through to the closing parenthesis. It is THEN executed, but with the %var%s replaced by their values at the time the endlocal&(etc) was encountered. & allows multiple commands on one physical line...
You want to read each of the columns in Text.txt and store it in variables, right?
Easily:
#echo off
set i=1
for /f "delims= ; tokens=1-3" %%a in (Text.txt) do (
set ordner%i%=%%a
set job%i%=%%b
set input%i%=%%c
set /a i=i+1
)
This code will set variables like this:
set ordner1=Ordner1
set job1=Job1
set input1=Input1
set ordner2=Ordner2
set job2=Job2
set input2=Input2
...
From your calling file (CALLREADLIST.BAT) you can access any column on any line by simply using %columnLine% (e.g: %job26% where "job" is your column and "26" the line number).
For instance, if you want CALLREADLIST.BAT to output the "Input" at line 45 all you'd need would be:
#echo off
readlist
echo %input45%
The first block is not working just because you're assigning to variable names their own values, which is null (set var=%var%, i.e. set var= which undefines the variable).

Batch file for loop unable to assign values from external file

So, I've got this basic text file called prev_batch.cfg that I want a batch script to read. prev_batch.cfg is a single line of text, but tokens are delimited with a comma. It looks something like:
apples,oranges,bananas,grapes,strawberries
I'd like each of those tokens to go into a specific variable I have setup in a for loop. The problem is only the first variable gets set within the loop. Nothing else. I want all the variables to be set to the tokens found within the delimited list in the .cfg file.
Here is what my batch script looks like up until the close of the FOR loop:
#ECHO ON
setlocal EnableDelayedExpansion
SET JOB=%1
SET USER=%2
SET ASSETROOT=%3
:: Read all the arguments for this batch script from external text file
FOR /F "delims=," %%a IN (%ASSETROOT%\users\%USER%\%JOB%\prev_batch.cfg) do (
SET JOB=%%a
SET USER=%%b
SET TEMPLATE=%%c
SET ASSETROOT=%%d
SET SHAREADDR=%%e
SET SHARENAME=%%f
SET SHAREDRIVE=%%g
SET SHAREUSER=%%h
SET SHAREPASS=%%i
)
I'm running the batch file and passing three command line parameters into it. I'm outputting the echo of the batch when it runs to a log file. And this is what I'm seeing:
C:\Windows\system32>setlocal EnableDelayedExpansion
C:\Windows\system32>SET JOB=99D0FAA9-22B8-4FE7-9321-21F5587E8177
C:\Windows\system32>SET USER=10
C:\Windows\system32>SET ASSETROOT=C:\app
C:\Windows\system32>FOR /F "delims=," %a IN (C:\app\users\10\99D0FAA9-22B8-4FE7-9321-21F5587E8177\prev_batch.cfg) do (
SET JOB=%a
SET USER=%b
SET TEMPLATE=%c
SET ASSETROOT=%d
SET SHAREADDR=%e
SET SHARENAME=%f
SET SHAREDRIVE=%g
SET SHAREUSER=%h
SET SHAREPASS=%i
)
C:\Windows\system32>(
SET JOB=99D0FAA9-22B8-4FE7-9321-21F5587E8177
SET USER=%b
SET TEMPLATE=%c
SET ASSETROOT=%d
SET SHAREADDR=%e
SET SHARENAME=%f
SET SHAREDRIVE=%g
SET SHAREUSER=%h
SET SHAREPASS=%i
)
try this:
FOR /F "usebackq tokens=1-9 delims=," %%a IN ("%ASSETROOT%\users\%USER%\%JOB%\prev_batch.cfg") do (
SET JOB=%%a
SET USER=%%b
SET TEMPLATE=%%c
SET ASSETROOT=%%d
SET SHAREADDR=%%e
SET SHARENAME=%%f
SET SHAREDRIVE=%%g
SET SHAREUSER=%%h
SET SHAREPASS=%%i
)
[addition by PW in response to supplementary question]
SET init=Y
SET "fields=JOB USER TEMPLATE ASSETROOT SHAREADDR SHARENAME SHAREDRIVE SHAREUSER SHAREPASS"
FOR /F "usebackq" %%a IN ("%ASSETROOT%\users\%USER%\%JOB%\prev_batch.cfg") do (
IF DEFINED init FOR %%i IN (%fields%) DO SET "%%i="
SET init=Y
FOR %%i IN (%fields%) DO IF DEFINED init IF NOT DEFINED %%i SET "%%i=%%a"&SET "init="
)
To read from sequential lines, the tokens and delims clauses are not required since there's only one string on each line BUT that one string needs to be applied to various variables.
Complicating the matter is the fact that variables that need to be set are used in the filename being read to set them.
Hence, the init flag is set non-empty initially; the variables are set once the first data line is read.
The next not-yet-set target variable is then set and the init flag cleared to ensure only one variable is set per line read.
okay, I like batch files and this one had me going for a while. You've told it that you only want %%a so that's what you're getting.
As Endoro said (I was typing the same answer when he posted), including the "tokens" keyword tells it which tokens you want.
Token %%a, obviously is the first, but by also telling the command processor that you want tokens 1-9 it separates and assigns them to next alphabetical 'variable'.
And in this case, usebackq is superfluous
There is another approach to solve this problem via an array of variable names. This method have the advantage that any modification to the list of variables (the number of variables, their names, their positions, etc) is made in a very easy way modifing just one line in the Batch file.
#echo off
setlocal EnableDelayedExpansion
rem Define the list of variable names
set variables=JOB USER TEMPLATE ASSETROOT SHAREADDR SHARENAME SHAREDRIVE SHAREUSER SHAREPASS
rem Convert the list into an array of variable names
set i=0
for %%a in (%variables%) do (
set /A i+=1
set variable[!i!]=%%a
)
set numVariables=%i%
:: Read all the arguments for this batch script from external text file
rem Version 1: Single line of text, tokens delimited with comma
set i=0
for /F "delims=" %%a in (%ASSETROOT%\users\%USER%\%JOB%\prev_batch.cfg) do (
for %%b in (%%a) do (
set /A i+=1
for %%i in (!i!) do set !variable[%%i]!=%%b
)
)
rem Version 2: Values of the variables on separate lines
set i=0
for /F "delims=" %%a in (%ASSETROOT%\users\%USER%\%JOB%\prev_batch.cfg) do (
set /A i+=1
for %%i in (!i!) do set !variable[%%i]!=%%a
)
rem Display values of all variables
for %%i in (1,1,%numVariables%) do (
for %%v in (!variable[%%i]!) do echo %%v = !%%v!
)
In the first version, the line of the file is read in %%a replaceable parameter and then processed in a for command this way:
for %%b in (%%a) do (
For example:
for %%b in (apples,oranges,bananas,grapes,strawberries) do (
Because standard Batch delimiters are space, comma, semicolon and equal-signs, the values in the list are processed one by one in the for (with NO /F option). This method allows to easily separate a list of values in individual tokens in a regular for command, but works only with the indicated delimiters.

Creating multiple files using batch files

Hi Can anyone help me out in this problem.
I need to create multiple file?? i give with any example. In some folder, say Folder Records.
"Record" folder contain 1 file by name "example2tought1023.au" . i need to generate same file contains, multiple time just by increasing the numbers.
i should get result like this example2tought1023.au example3tought1024.au example4tought1025.au example5tought1026.au
This is what I currently have:
SET count=9
SET filename_1=example
SET filename_2=thought
SET extension=.au
SET start_1=2
SET start_2=1023
SET source=%filename_1%%start_1%%filename_2%%start_2%%extension%
FOR /L %%i IN (1, 1, %count%) DO (
REM These two lines do not work!
SET /a n=%start_1%+%%i
SET /a number_2=%start_2% + %%i
SET destination=%filename_1%%number_1%%filename_2%%number_2%%extension%
ECHO %destination%
REM COPY %source% %destination%
)
PAUSE
but the lines in the FOR /L loop do not work
You have mis-identified which lines are not working :-)
The problem you are having relates to when variables are expanded. Normal expansion using percents occurs when the line is parsed, and your entire FOR statement, including the parenthesised DO clause, is parsed in one go. So the following line
SET destination=%filename_1%%number_1%%filename_2%%number_2%%extension%
is seeing the values of %number_1% and %number_2% that existed before the loop was executed. Obviously not what you want. The solution is simple - you need to use delayed expansion (the value at run time instead of parse time). You do that by 1st enabling delayed expansion using setlocal enableDelayedExpansion, and then use !number_1! instead of %number_1%.
You are not consistent with your variable names (n vs number_1).
I think you want to count from 0 to count-1 instead of from 1 to count.
You do not have to explictly expand the variable when using a variable in a SET /A expression. You can simply use the variable name without percents or exclamations. But this only works with the SET /A command.
You can also perform multiple computations and assignments with a single SET /A command by using a comma between each assignment.
#echo off
setlocal enableDelayedExpansion
REM counts from 0 to count, so 8 = 9 copies
set count=8
set filename_1=example
set filename_2=thought
set extension=.au
set start_1=2
set start_2=1023
set source=%filename_1%%start_1%%filename_2%%start_2%%extension%
for /L %%i in (0, 1, %count%) do (
set /a "number_1=start_1+%%i, number_2=start_2+%%i"
set destination=%filename_1%!number_1!%filename_2%!number_2!%extension%
echo !destination!
REM copy %source% !destination!
)
pause

Resources