How to set and use global array variable in Batch file - batch-file

Here is my code for storing the words extracted from a log file into an array, which I want to use in the batch file later on.
cls
#echo off
set /a i=0
TIMEOUT 2
REM I want to save the words from newlog.txt into an array for later use in batch file.
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=1,2,3 delims= " %%G IN (newlog.txt) DO ( set a[%i%]=%%H
& set /a i+=1 #echo !a[%i%]! )
#echo %i%
#echo a[%i%]
TIMEOUT 200
I just want to use them as global variables.

#echo off
set /a i=0
REM I want to save the words from newlog.txt into an array for later use in batch file.
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=1,2,3 delims= " %%G IN (q48428047.txt) DO (
set a[!i!]=%%H&set /a i+=1)
SET a[
pause
GOTO :EOF
I used a file named q48428047.txt containing some dummy data for my testing.
Note that %i% will be replaced by the value of i *as it was when the *forwas encountered that is, 0.
Your second set within your parentheses is incorrect. It's missing the & before the #echo. Also, you appear to be attempting to increment i and then show the corresponding array entry, which makes no sense because you haven't yet stored anything in that array-entry, you've attempted to install it in the previous position. It won't work anyway, because %i% will be replaced by 0, not the varying value of i.
And whereas you may get the count-of-lines appearing on the screen, a[numlines] will not be defined.
Use pause to stop the batch for perusal. Preferably, run the command from the prompt, not by clicking.
The modified code reads each line of the file putting the first word in %%G, second in %%H and third in %%I. it then assigns %%H to a[currentlinenumber] and increments the line number in i.
The set a[ will display all environment variables whose names start with a[.

In addition to the answer already provided, if you really have a need to Echo each variable value immediately after it is Set then you would need to invoke a Call command:
#Echo Off
Rem Undefine any existing variables which begin with a[
For /F "Delims==" %%A In ('"(Set a[) 2>Nul"') Do Set "%%A="
Set "i=0"
ClS
SetLocal EnableDelayedExpansion
For /F "UseBackQ Tokens=2 Delims= " %%A In ("newlog.txt") Do (
Set "a[!i!]=%%A"
Call Echo "%%a[!i!]%%"
Set /A i+=1
)
If %i% GEq 1 Echo(&Echo %i% variables were defined&Echo(
(Set a[) 2>Nul
Timeout 200

Related

How do you use a batch to modify certain values in a .txt file?

I have a .txt file which contains 1 line in the following format:
XX-C-0001
I want to save a new file with the incremented number, like:
XX-C-0002
The Problem is, I dont know how to save the "0002" as a variable
Heres what i come up with:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /f "tokens=3" "delims=-" %%a in name.txt(
'VER'
) do (
set myvar=%%a
)
set /A var=%myvar%+1
>new_name.txt echo %var%
pause
endlocal
Check your for syntax (it's off at some points, but where did 'ver' come from?)
To increase the number, you have to be a bit more creative because of the leading zeros. Put a 1 in front of it (make it 1000x), then increase it and use just the last four characters for the result.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /f "tokens=3 delims=-" %%a in (name.txt) do set "myvar=1%%a"
set /A myvar+=1
>new_name.txt echo XX-C-%myvar:~-4%
type new_name.txt
endlocal

Batch file question: How can I load a text table from a file (with exclamation marks) into a global array?

My goal is to load a text file into a global array, and the file contains exclamation marks. I want to then bubble sort the array, and save the contents to a new file. Unfortunately I cannot even get past loading data into an array. I tried using for loops, but it seems delayed expansion is both needed and not needed. Of course, any variables made inside the loops are local variables due to the setlocal command, correct? So once endlocal is reached (in order to toggle delayed expansion), the scope is gone. I was thinking that I need the array to be global so that I can continue manipulating it later on. (I tried to add set commands at the end of endlocal to pass the local values up to higher scope, but my attempts got me nowhere.)
setlocal enabledelayedexpansion
set /a count=0
set "file=list.txt"
echo Extracting titles and IDs...
for /f "usebackq delims=; tokens=1,2* skip=1" %%a in ("%file%") do (
set /a count+=1
setlocal disabledelayedexpansion
set "title=%%b"
set "gameid=%%a"
setlocal enabledelayedexpansion
for /f "delims=- tokens=1,2*" %%x in ("!gameid!") do (
echo %%y %%x "!title!"
set "entry[!count!]=%%y %%x !title!"
echo %count% - !count!
)
endlocal
endlocal
)
pause
The input file looks like:
ID;Title;Other;Headings;We;Dont;Need
ABC-456;My second game ever! Even better!;foo;bar;blah;blah;blah
XYZ-123;My first game ever! Yes!;foo;bar;blah;blah;blah
My end goal is to create a file in the form
123 XYZ "My first game ever! Yes!"
456 ABC "My second game ever! Even better!"
after sorting by the numerical values. If anyone could help just get through the first part (loading data into an array), that would be greatly appreciated.
You can avoid delayed expansion completely by not using variables, when you can use for variables.
The only spot that would require delayed expansion, can be done by an alternative:
call set "entry[%%count%%]=%%Y %%X %%B"
call forces another layer of expansion. for that to work properly, you have to make sure, %%var%% doesn't get confused with %%v and ar%%. That's the reason, I changed the for variable to uppercase (%%a is different from %%A).
#echo off
setlocal disabledelayedexpansion
set /a count=0
set "file=list.txt"
echo Extracting titles and IDs...
for /f "usebackq delims=; tokens=1,2* skip=1" %%A in ("%file%") do (
set /a count+=1
for /f "delims=- tokens=1,2*" %%X in ("%%A") do (
call set "entry[%%count%%]=%%Y %%X %%B"
)
)
(for /f "tokens=1,* delims==" %%A in ('set entry[') do #echo %%B)|sort
pause
(Note: the sorting works only, when all the items have a three-digit number)
Output with your example data:
Extracting titles and IDs...
123 XYZ My first game ever! Yes!
456 ABC My second game ever! Even better!

Double Expansion on Array Index Windows Batch

Inside the for loop I'm trying to access the element at index count in CLs (this line of code: echo !!CLs[!count!]!!) , but I'm not sure how to do this. I don't really understand how expansion works in this case, so what you see below it me trying something out of no where.
#ECHO off
setlocal enableextensions enabledelayedexpansion
SET CLs[0]=#
SET /A count = 0
FOR /F "tokens=5" %%I IN ('some command') DO (
echo !!CLs[!count!]!! :: THIS LINE
IF NOT %%I == CLs[!count!] (
SET /A count += 1
SET CLs[!count!]=%%I
)
)
echo The item is %CLs[10]%
endlocal
Thanks
According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts? (see phase 5), the line echo !!CLs[!count!]!! cannot work, because the opening !! are collapsed to a single !, then !CLs[! is expanded to an empty string (assuming such variable is not defined), then count is returned literally, then !]! is expanded to an empty string and the final ! is dismissed. Or in other words, delayed expansion cannot be nested.
You can use call though to introduce another parsing phase, like this:
call echo %%CLs[!count!]%%
The line IF NOT %%I == CLs[!count!] ( ... ) is wrong, you must expand the right value too. However, call if will not help unfortunately, because if (like for and rem) is a special command that is recognised by the parser earlier than others, like call.
To work around that you can store the value of !count! in a for meta-variable, like %%J, for instance, to introduce another parsing phase, and use !CLs[%%J]! then, like this:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
for %%J in (!count!) do (
echo !CLs[%%J]!
if not "%%I" == "!CLs[%%J]!" (
set /A "count+=1"
set "CLs[!count!]=%%I"
)
)
)
Another yet slower possibility is to put the relevant code into a sub-routine:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
call :SUB !count!
)
goto :EOF
:SUB
echo !CLs[%~1]!
if not "%%I" == "!CLs[%~1]!" (
set /A "count+=1"
set "CLs[%~1]=%%I"
)
goto :EOF
You may also take a look at the post Arrays, linked lists and other data structures in cmd.exe (batch) script about how to deal with such pseudo-arrays.
ECHO ------------- START AT %time%
REM <!-- language: lang-dos -->
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /f "tokens=1,2,3delims=[]=" %%a IN ('set cls[') DO IF /i "%%a"=="cls" (
IF "%%I"=="%%c" (SET /a clscnt[%%b]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
ECHO -------------------------Second way -----------------
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /L %%a IN (0,1,!count!) DO (
IF "%%I"=="!cls[%%a]!" (SET /a clscnt[%%a]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
GOTO :EOF
I used a file named q58209698.txt containing some dummy data for my testing and chose to use the entire data line, having no suitable files where token 5 existed.
Note that as a bonus, I've added clscnt - an array of occurence-counts.
Shown: two separate ways of achieving the aim of finding/counting the unique tokens. Naturally, if the cls array is pre-loaded with the required tokens, then it's basic-programmer's-play to adjust the code to detect/report occurrences of those tokens.
The two methods are similar. In the first, set is used to list the established variables starting cls[. The first if ensures processing only the array-name cls, then either it's a repeat (set prcoessed to a value and increment the occurrences-counter) or it's a new value (when the for...%%a loop ends, processed is still undefined) so record it.
The second way is more direct, using the value of count to specifically interrogate the values in the cls array.

Batch File - loop incrementing count in value not displaying correctly

I'm trying to read a file and output the lines of data into registry keys. The data collection works, but I don't understand the syntax required to increment the string values in the last loop.
#echo OFF
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq skip=1 delims=" %%a in (`"findstr /n ^^ C:\GetSID.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!" This removes the prefix
echo(!var:~76,63!>>C:\SIDoutput.txt
goto :EndLoop
)
:EndLoop
set /p SID= <C:\users\paintic\SIDoutput.txt
set KEY_NAME="HKEY_USERS\!SID!\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts"
set Counter=1
for /f %%x in (C:\users\paintic\Networkprinters.txt) do (
set "Line_!Counter!=%%x"
set /a Counter+=1
if !Counter!==3 (Echo %line_counter%)
)
set /a counter2=!counter!-3
set counter=1
The part below is what I can't get to work. I'm trying to write LINE_1, LINE_2 and LINE_3 values from the previous loop to increment via the loop below. So VALUENAME should equal LINE_1, TYPE should = LINE_2's value and DATA should = LINE_3 on the first run and keep going up by 1 until the loop finishes (end of the file read)
`for /L %%i in (1,1,%counter2%) do (
set ValueName=%Line_!counter!%
set /a counter+=1
set Type=%Line_!counter!%
set /a Counter+=1
set Data=%Line_!counter!%
set /a Counter+=1
echo !ValueName!
echo !Type!
echo !Data!
REG ADD %KEY_NAME% /v !ValueName! /t !Type! /d !Data! /f
)
ENDLOCAL
Pause`
On searching for errors in batch file it is always helpful to use in first line #echo on or remove #echo off or comment this line with rem to see what cmd.exe really executes.
Command line interpreter fails on lines with set VariableName=%Line_!counter!% as the interpreter does not know what to expand first. I think it is not possible to create dynamically the name of an environment variable and reference next the value of this environment variable. This approach most likely does not work ever.
However, what you want to achieve can be done much easier directly in second loop as the following example demonstrates:
#echo off
setlocal EnableDelayedExpansion
rem Create data for demo example.
set "KEY_NAME=HKEY_USERS\S-1-5-20\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts"
echo TestValue>"%TEMP%\Networkprinters.txt"
echo REG_SZ>>"%TEMP%\Networkprinters.txt"
echo Sample Data>>"%TEMP%\Networkprinters.txt"
echo AnotherValue>>"%TEMP%\Networkprinters.txt"
echo REG_DWORD>>"%TEMP%\Networkprinters.txt"
echo ^1>>"%TEMP%\Networkprinters.txt"
rem Now the loop follows which reads the data from the file line
rem by line and build the line for using command "reg.exe" to
rem add the data to registry of the user with the defined SID.
set Counter=1
for /f "usebackq delims=" %%x in ("%TEMP%\Networkprinters.txt") do (
if "!Counter!"=="1" (
set "ValueName=%%x"
) else if "!Counter!"=="2" (
set "ValueType=%%x"
) else (
set "ValueData=%%x"
rem Echo the command instead of really executing "reg.exe".
echo reg.exe ADD %KEY_NAME% /v "!ValueName!" /t !ValueType! /d "!ValueData!" /f
set Counter=0
)
set /a Counter+=1
)
rem Delete the text file created for demo example.
del "%TEMP%\Networkprinters.txt"
endlocal
This solution is much easier than what you have tried and can be maybe even more simplified.

Windows Batch: Loop through to echo a set of variables

I have a batch file that contains the code below which assigns a variable for each line in a text file.
So the text file might have:
RELEASE1
RELEASE2
RELEASE3
The batch file sets each line in the text file to var1, var2, var3 with the following code:
#echo off
setlocal ENABLEDELAYEDEXPANSION
set vidx=0
for /F "tokens=*" %%A in (sites.txt) do (
SET /A vidx=!vidx! + 1
set var!vidx!=%%A
)
I need a method to echo all the defined variables. The number of variables will always change. So it might go up to var8 or var10 etc...
I'm pretty certain a for loop would do the trick but not sure what the best approach or how to do it? I was thinking of using vidx as the number of iterations? Thanks for your help!
Very Easy
#echo off
setlocal ENABLEDELAYEDEXPANSION
set count=0
for /F "tokens=*" %%A in (sites.txt) do (
SET /A count+= 1
set var!count!=%%A
)
Rem //:Notice That %count% still stores the last variable, use this in a "for /l" loop:
for /l %%a in (1,1,%count%) do (
Rem Below line is optional and displays variable number
<nul set /p="Variable %%a: "
Echo !var%%a!
)
That should work fine as long as none of the data contains parenthesis, in which case you'll have to escape them.
Type for /? for help on this. Ask If you want an explanation.
set var|findstr /R "var[0-9]*="
May be the shortest way....

Resources