Batch Programming: read txt and keep variables with a subroutine - batch-file

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).

Related

Batch file set to pick a random file from preset categories is not picking a random file (keeps picking the last file!)

Good evening. I enjoy modding a game in my free time, it's one those games where individual mods are packed into zip files and loaded in by simply placing said zips into a certain folder. I have hundreds of these zips and was hoping to make a batch that would pick out a random mod for me each time I use it instead of having to pick through them all myself, while placing all the unused ones into a disabled folder that I made to keep everything not being used all in one place. Every mod was prefixed with a name referring to what category it'd belong to, for example 'weapon_shotgun_GiantAngryGun9000.zip' or 'character_huck_Clown.zip'
How this batch script would work is that first it'd take every file with the specified prefixes currently in the mod folder and put them all into the disabled folder. It would then go through every single file in the disabled folder that has all the specified prefixes, pick a random file from each category, and move them into the mod folder. Currently I've gotten my batch to do all that is explained, except rather than pick a random of each, it keeps picking the alphabetically last one from each prefix list. I believe it's because the Num var being used to designate the index for the entries in the File array is not being modified or read, and in-spite my best efforts I cannot get it to work the way I'm hoping.
Any advice or solutions is appreciated, thank you for reading.
setlocal EnableDelayedExpansion
SET modFolder="C:\Game\Mods\"
SET disabledFolder="C:\Game\Mods\Disabled\"
SET itemListIndex=0
::Prefix names
SET Prefix1=Weapon_Shotgun_
set itemList[%itemListIndex%]=%Prefix1%
set /A itemListIndex+=1
SET Prefix2=Character_ThisPerson_
set itemList[%itemListIndex%]=%Prefix2%
set /A itemListIndex+=1
SET Prefix3=Weapon_Rifle_
set itemList[%itemListIndex%]=%Prefix3%
set /A itemListIndex+=1
for /l %%G in (0 1 %itemListIndex%) do (
if not "!itemList[%%G]!" == "" (
set num=0
for %%f in (!itemList[%%G]!*.zip) do (
set /A num+=1
set file[%num%]=%%f
)
set /A pick = !random! * %num% / 32768 + 1
echo Moving "!file[%pick%]!" to the mod folder
move "%disabledFolder%!file[%pick%]!" %modFolder%
)
)
set file[%num%]=%%f is echoed as set file[]='[name of file]' instead of the expected set file['(index number)']='[name of file]'
the written echo says Moving !file[]! to the mod folder instead of the expected Moving (name of file) to the mod folder
The pick variable echos as blank, implying num or random are not valid
#ECHO OFF
setlocal EnableDelayedExpansion
SET modFolder="C:\Game\Mods\"
SET disabledFolder="C:\Game\Mods\Disabled\"
SET itemListIndex=0
::Prefix names
SET Prefix1=Weapon_Shotgun_
set itemList[%itemListIndex%]=%Prefix1%
set /A itemListIndex+=1
SET Prefix2=Character_ThisPerson_
set itemList[%itemListIndex%]=%Prefix2%
set /A itemListIndex+=1
SET Prefix3=Weapon_Rifle_
set itemList[%itemListIndex%]=%Prefix3%
set /A itemListIndex+=1
for /l %%G in (0 1 %itemListIndex%) do (
if not "!itemList[%%G]!" == "" (
set num=0
for %%f in (!itemList[%%G]!*.zip) do (
set /A num+=1
set file[!num!]=%%f
)
set /A pick = !random! * !num! / 32768 + 1
CALL SET "PICKFILE=%%file[!pick!]%%"
REM echo Moving "!file[%pick%]!" to the mod folder
REM ECHO move "%disabledFolder%!file[%pick%]!" %modFolder%
echo Moving "!pickfile!" to the mod folder
ECHO move "%disabledFolder%!pickfile!" %modFolder%
)
)
GOTO :EOF
Your issue is that %num% and %pick% are the values of num and ``pick` when the outer loop (%%G) was parsed, that is, nothing.
The classic method is to use !num! for the run-time value, but you are using indirection, so you'd be tempted to use !file[!pick!]! which is ambiguous and resolved to pick. The call uses a subshell to resolve the ambiguity.
Move command disarmed by being echoed.
Despite having already received and accepted an answer, I have decided to post this to show you how I would do it.
#Echo Off
SetLocal EnableDelayedExpansion
Set "modFolder=C:\Game\Mods"
Set "disabledFolder=C:\Game\Mods\Disabled"
Rem Undefine existing variables
For /F "Delims==" %%A In ('Set item 2^>NUL') Do Set "%%A="
Rem Define filename prefixes
Set "itemListIndex=0"
For %%A In (
Weapon_Shotgun
Character_ThisPerson
Weapon_Rifle
) Do (Set /A itemListIndex+=1
Set "itemList[!itemListIndex!]=%%A_")
Rem Randomize itemListIndex
Set /A itemListIndex=(%RANDOM% %% itemListIndex)+1
Rem Define files based upon itemList[%itemListIndex%]
Set "itemFileIndex=0"
For %%A In ("!itemList[%itemListIndex%]!*.zip") Do (Set /A itemFileIndex+=1
Set "itemFile[!itemFileIndex!]=%%A")
Rem Randomize itemFileIndex
Set /A itemFileIndex=(%RANDOM% %% itemFileIndex)+1
Rem Move itemFile[%itemFileIndex%] to modFolder.
Echo Moving "!itemFile[%itemFileIndex%]!" to the mod folder
Move /Y "%disabledFolder%\!itemFile[%itemFileIndex%]!" "%modFolder%"
Pause
This way, you can easily append to the parenthesized list of prefixes, (which both significantly reduces your code and makes it simpler to maintain). It also makes more sense to randomize itemListIndex first, to reduce the number of returned .zip files for selection/moving.

Batch FOR loop without setlocal enabledelayedexpansion

using this code for example :
setlocal enabledelayedexpansion
set arrayline[0]=############
set arrayline[1]=#..........#
set arrayline[2]=#..........#
set arrayline[3]=#..........#
set arrayline[4]=#..........#
set arrayline[5]=#..........#
set arrayline[6]=#..........#
set arrayline[7]=#..........#
set arrayline[8]=#..........#
set arrayline[9]=#..........#
set arrayline[10]=#..........#
set arrayline[11]=#..........#
set arrayline[12]=############
::read it using a FOR /L statement
for /l %%n in (0,1,12) do (
echo !arrayline[%%n]!
)
pause
I want to use "normal" variable syntax instead of using setlocal enabledelayedexpansion (line 1).
I mean that instead of !arrayline[%%n]! I want to have something like that : %arrayline[n]% in order to put the information in it, in a variable (this way:)
setlocal enabledelayedexpansion
set arrayline[0]=############
set arrayline[1]=#..........#
set arrayline[2]=#..........#
set arrayline[3]=#..........#
set arrayline[4]=#..........#
set arrayline[5]=#..........#
set arrayline[6]=#..........#
set arrayline[7]=#..........#
set arrayline[8]=#..........#
set arrayline[9]=#..........#
set arrayline[10]=#..........#
set arrayline[11]=#..........#
set arrayline[12]=############
::read it using a FOR /L statement
for /l %%n in (0,1,12) do (
set buffer=%arrayline[n]%
echo %buffer%
)
pause
I NEED to put the information in a variable (which is buffer here).
If possible, can somebody replace the necessary code in mine ?
I'm a begginer so if it is not clear enough just ask ;)
Thank you in advance.
CALL echo %%arrayline[%%n]%%
is the classic solution here. This executes echo %arrayline[n]% in a subprocedure.
CALL SET "myvar=%%arrayline[%%n]%%"
CALL ECHO %%myvar%%

Need to copy text from one file to another in batch file

Need to copy text from one file to another.
file1 as below:
locked/agent.jms.remote.host=<AGENTHOST>
locked/agent.jms.remote.port=<AGENTPORT>
file2 will be
locked/agent.jms.remote.host=mkdfvsh_a-2341
locked/agent.jms.remote.port=1234
& need to replace when creating the second file
command i used:
SET newfile=file2
SEt filetoCOpy=file1
for /f "tokens=*" %%i in (%filetoCOpy%) do (
SET "line=%%i"
SETLOCAL EnableDelayedExpansion
SET line=!line:<AGENTHOST>=%AGENTHOST%!
SET line=!line:<AGENTPORT>=%AGENTPORT%!
echo !line!>>%newfile%
)
Result i got
locked/agent.jms.remote.host=<AGENTHOST>
locked/agent.jms.remote.port=<AGENTPORT>
variable value not changing.
Can someone help here what's wrong?
You should be getting an error with the code you posted. The < and > are treated as redirection unless they are escaped or quoted:
SET line=!line:^<AGENTHOST^>=%AGENTHOST%!
SET line=!line:^<AGENTPORT^>=%AGENTPORT%!
or
SET "line=!line:<AGENTHOST>=%AGENTHOST%!"
SET "line=!line:<AGENTPORT>=%AGENTPORT%!"
But you still have at least one problem - If you enable delayed expansion within a loop, then you must ENDLOCAL at the end of the loop, otherwise you can run out of SETLOCAL stack space.
Also, I don't see where AGENTHOST or AGENTPORT is defined.
You can try something like that :
#echo off
SET newfile=file2.txt
SEt filetoCOpy=file1.txt
set "ReplaceString1=mkdfvsh_a-2341"
set "ReplaceString2=1234"
If exist %newfile% Del %newfile%
for /f "tokens=*" %%i in (%filetoCOpy%) do (
SET "line=%%i"
SETLOCAL EnableDelayedExpansion
SET line=!line:^<AGENTHOST^>=%ReplaceString1%!
SET line=!line:^<AGENTPORT^>=%ReplaceString2%!
echo !line!>>%newfile%
)
EndLocal
start "" %newfile%

Batch FOR loop IF and SET conflict

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%

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.

Resources