Double Expansion on Array Index Windows Batch - arrays

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.

Related

Variables are not set properly inside nested loops

I have a similiar question like I did ask here: Nested loop
However, I still did not solve the "problem" which seems to be little. I already implemented the solution mentioned using [FindStr][2], however the runtime is much much longer than without FindStr. So I would like to keep the method using
IF not "!stringToTest:!searchValue!=!"=="!stringToTest!" echo Something
Below I post some runable code. In the beginning I just set my array with the values I would like to search for and then some exmaple "texts" in which I like to search my beforehand set searchvalues.
My strategy:
Loop through my file with the text lines. For each line test each SearchValue for presence.
After all Searchvalues tested, go to ne next line and check also for the presence of each searchvalue.
So I have two nested Loops. At every step inside the loop I output the current vaule of the variables and those seems to be correct. I really do not know how my fault in this line is as the searchfunction is not working correctly:
IF not "!stringToTest:!searchValue!=!"=="!stringToTest!" echo Searchword is in Textline
There may be just a little mistake in here? I am very thankful for every tipp in this issue.
#echo off
setlocal enabledelayedexpansion
set /a counter=0
set "searchValues=abc,xyz"
FOR %%G in (%searchValues%) do (
set arraySearchVal[!counter!]=%%G
set /a counter+=1
)
REM set arraySearchVal
set /a counter-=1
set "TextWhichNeedToBeSearched=tzu,abc,qsd"
FOR %%G in (%TextWhichNeedToBeSearched%) do (
set "stringToTest=%%G"
echo Textline: !stringToTest!
FOR /l %%I in (0,1,%counter%) do (
set "searchValue=!arraySearchVal[%%I]!"
echo Searchword: !searchValue!
REM echo !stringToTest! found in Textline | findstr "!searchValue!"
IF not "!stringToTest:!searchValue!=!"=="!stringToTest!" echo Searchword is in Textline
)
echo/
)
endlocal
pause
This falls under the K.I.S.S. methodology. By making your search arguments array variables you have over complicated your code. You can simplify it like so.
#echo off
setlocal enabledelayedexpansion
set "searchValues=abc,xyz"
set "TextWhichNeedToBeSearched=tzu,abc,qsd"
FOR %%G in (%TextWhichNeedToBeSearched%) do (
set "stringToTest=%%G"
echo Textline: !stringToTest!
FOR %%I in (%searchValues%) do (
echo Searchword: %%I
IF not "!stringToTest:%%I=!"=="!stringToTest!" echo Searchword is in Textline
)
echo/
)
endlocal
pause
If you really want to use your original code you can get the double variable expansion using a trick with the CALL command to set another variable. Then use that variable with the IF command.
#echo off
setlocal enabledelayedexpansion
set /a counter=0
set "searchValues=abc,xyz"
FOR %%G in (%searchValues%) do (
set arraySearchVal[!counter!]=%%G
set /a counter+=1
)
REM set arraySearchVal
set /a counter-=1
set "TextWhichNeedToBeSearched=tzu,abc,qsd"
FOR %%G in (%TextWhichNeedToBeSearched%) do (
set "stringToTest=%%G"
echo Textline: !stringToTest!
FOR /l %%I in (0,1,%counter%) do (
set "searchValue=!arraySearchVal[%%I]!"
echo Searchword: !searchValue!
REM echo !stringToTest! found in Textline | findstr "!searchValue!"
call set "temptest=%%stringToTest:!searchValue!=%%"
IF not "!temptest!"=="!stringToTest!" echo Searchword is in Textline
)
echo/
)
endlocal
pause
Before getring into this too much, why are you creating a list of counter values if you don't need to? Why bot loop the 1st set and 2nd set wirhoutbhaving special variables for a for L loop, as you don't then assign the positional value.
Another bit, are all of the terms you are looking to see if exist in the text strings partials? Or are you always going to match the full string? If you are matching full strings you only need one loop.
IE if possible strings to match are the Full value, or "whole word" you want to find in the set to look through.
EG
REM this sees if any of the arguments match the list of options using only one loop.
Set "_OptionsToMatch= -D -Delete -R -Remove "
FOR %%_ in (%*) DO (
Echo=%_OptionsToMatch% | FIND /I " %%~_ " )
Assuming the above doesn't work for you in this scenario for some reason
Well lets address the other bits like why are you crwating a valued array at all?
#(echo off
setlocal enabledelayedexpansion
set "searchValues=abc,xyz"
set "TextWhichNeedToBeSearched=tzu,abc,qsd"
)
CALL :Main
( Endlocal
Pause
Exit /B
)
:Main
FOR %%G in (%TextWhichNeedToBeSearched%) do (
SET "stringToTest=%%G"
FOR %%g in (%searchValues%) do (
IF /I "!stringToTest:%%g!" NEQ "%%G" (
Echo The string "%%G" contains "%%g"
) ELSE (
Echo The string "%%G" does not contain "%%g"
)
)
)
Goto :EOF
If all that were needed you could amend the above to make it work to have numbered arrays as it goes along I suppose.
Alternatively if you just want to address what is going on:
Delayed expansion isn't a complete replacement for calling a command or using a function.
This is one of the reasons Inused to only write scripts wirhout delayed expansion as even wirh it on mor complex work still requires calling a function to do the steps as part of the loop.
This is generally when doing lots of value man up such as here.
The alternative, which works as well is to create loops to do the next level of variable manipulation, which is faster? YMMV
Which is simpler - generally calling the sub function.
Which is easier to follow - YMMV.
Here is the sub function method:
#(echo off
setlocal enabledelayedexpansion
set /a "counter=0"
set "searchValues=abc,xyz"
set "TextWhichNeedToBeSearched=tzu,abc,qsd"
)
CALL :Main
( Endlocal
Pause
Exit /B
)
:Main
FOR %%G in (%searchValues%) do (
set /a "counter+=1"
set "arraySearchVal[!counter!]=%%G"
)
FOR %%G in (
%TextWhichNeedToBeSearched%
) do (
set "stringToTest=%%G"
echo Textline: !stringToTest!
FOR /l %%I in (1,1,%counter%) do (
Call set "searchValue=!arraySearchVal[%%I]!"
echo Searchword: !searchValue!
CALL :Fn_Compare
If !_CmpResult! == 0 (
Echo=the string did not exist.
Echo=Even in your loop you can use this result now.
) ELSE (
Echo=the string does exist.
Echo=Even in your loop you can use this result now.
)
)
echo/
)
Goto :EOF
:Fn_Compare
REM echo !stringToTest! found in Textline | findstr "!searchValue!"
IF /I "!stringToTest:%searchValue%=!" NEQ "!stringToTest!" (
echo Searchword is in Textline
Set "_CmpResult=1"
) ELSE (
Set "_CmpResult=0"
)
Goto :EOF
Is this not all you need?
#Echo Off
Set "TextWhichNeedToBeSearched=tzu,abc,qsd"
Set "SearchValues=abc,xyz"
Set "ResultString=Searchword is in Textline"
For %%# In (%SearchValues%)Do (
Echo Textline: %TextWhichNeedToBeSearched%
Echo Searchword: %%#
Echo("%TextWhichNeedToBeSearched%"|FindStr "\<%%#\>">NUL 2>&1&&(
Echo(%ResultString%)||Echo(%ResultString:is=is not%
Echo(
)
Pause

Extract a specific portion from a string(filename) using batch file

I am trying to extract a portion of all the filenames(pdf files) in the current directory.
The length of filenames vary except for the last portion(datetime and extension) which will always be 16 characters. The remaining part will always have different lengths. Even the portion I require may have varying lengths.
I tried using lastIndexOf function obtained here.
filename eg : academyo-nonpo-2582365-082416051750.pdf
I want to extract the section in Bold.
I tried trimming the last 17 characters(this portion will always have a fixed length.) first and then tried to obtain the last Index Of '-'(since the fist portion can have variable character length.) and trim the characters until that position, which should return the required portion of the filename.
#echo off
Setlocal enabledelayedexpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call :lastindexof !File! - lio2
Set "File=!File:~%lio%!"
)
Pause&Exit
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal enableDelayedExpansion
set "str=%~1"
set "p=!str:%~2=&echo.!"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
set argv=original
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
echo -1
)
setlocal DisableDelayedExpansion
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
%$strlen% strlen,str
%$strlen% plen,last_part
%$strlen% slen,splitter
set /a lio=strlen-plen-slen
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b
The reference of the variable passed to the function as the 3rd parameter doesn't seem to be returning the required value.
I dunno what is wrong here.
To get the section in bold then:
Example#
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~-20,7!"
Echo=!File!%%~x#)
Pause
Okay what about?
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~,-13!"
Call :Sub "!File:-=\!%%~x#")
Pause
:Sub
Echo=%~nx1
To extract the portion in between the last hyphen and the next-to-last one, you could use the following script (provide the strings/files as command line arguments):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "SEP=-"
for %%A in (%*) do (
set "ITEM=%%~A"
set "PREV="
if defined ITEM (
for %%B in ("!ITEM:%SEP%=" "!") do (
set "PREV=!PART!"
set "PART=%%~B"
)
if defined PREV (
echo(!PREV!
)
)
)
endlocal
exit /B
This approach basically replaces every - by the standard cmd tokenisation character SPACE and iterates through the resulting string using a standard for loop (no /F option). The currently iterated part is stored in variable PART, whose content is first copied into PREV to gain a delay of one loop iteration. So the next-to-last portion is finally stored in PREV.
Note that this script might return unexpected results in case the strings/files contain exclamation marks because of delayed expansion.
Have a look on this answer. Thought is to first count the number of tokens (you still do have to trim the string before this) and then get the last token.
In the first loop where it says "tokens=1*" , you have to edit it to the following: "tokens=1* delims=-" and in the second loop add delims=- as well after %i%. It should be looking like this in total with your script:
#echo off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call:subfunction !File! - lio2
Set "File=!File:~%lio%!"
)
:subfunction
set var1=%1
set var2=%var1%
set i=0
:loopprocess
for /F "tokens=1* delims=-" %%A in ( "%var1%" ) do (
set /A i+=1
set var1=%%B
goto loopprocess )
for /F "tokens=%i% delims=-" %%G in ( "%var2%" ) do set last=%%G
echo %last%
REM do what you want with last here!
I tested it and it seems to be working correctly even with something like ac-ade-myo-n-on-po-15482729242321654-082416051750.pdf, however after finishing correctly, it give an error message one time with a syntax error I could not find...
If you can ignore that error (everything else works), this might help.

Rename first part of filename using batch

I have some problem writing a code for a batchfile that will replace the first part of a file name.
let say we have the files:
abcd123.txt
abcd345.txt
the numeric part(and the extensions) is the part I want to keep and change it to blabla123.txt and blabla345.txt
the numeric part is not always the same.
I tried to write:
set FILE =%1
set LastPart = %FILE:~-7%
set NewName = c:\MyFolder\blabla%LastPart%
ren %FILE% %NewName%
but it didn't worked because there's space between c:\MyFolder\blabla to 123.txt
Perhaps:
SET "OldName=%~n1"
SET "Ext=%~x1"
SET "LastPart=%OldName:~-3%"
SET "FirstPart=blabla
SET "NewFold=C:\MyFolder"
REN "%~1" "%NewFold%\%FirstPart%%LastPart%%Ext%"
Please see if below script helps you. It iterates through all files in a given directory and renames them according to your requirement.
#echo OFF
setlocal ENABLEDELAYEDEXPANSION
REM Get input directory from user
set /p INPUT_DIR=Please enter full path to directory with files, use double quotes if any space:
cd /d %INPUT_DIR%
for /f %%f in ('dir /b %INPUT_DIR%') do (
set newname=hello!fullname:~-7!
ren %%f !newname!
)
Output
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
adadadadad123.txt
E:>Temporary\SO\batch\test_ren.bat
Please enter full path to directory with files, use double quotes if any
space:"E:\Temporary\SO\batch\Input - Space"
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
hello123.txt
Although the question is not quite clear to me, I decided to provide an answer, because the task of extracting a numeric part from the end of a string appears not to be that trivial, particularly in case both the preceding string and the numeric portions may have different lengths.
So here is a script that accepts file paths/names/patterns provided as command line arguments, splits off ther numeric part, prepends an optional prefix to it and renames the file accordingly (actually it just echoes the ren command line for testing; remove the upper-case ECHO to actually rename):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
if 1!NAME! GTR 1!FNEW! (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
The script skips all files that have less or more than exactly one numeric part in their names, and also those where the numeric part is followed by something other than the file name extension. For example, abcd1234.txt is processed, whereas abcd.txt, 1234.txt, ab1234cd.txt, 1234abcd.txt and ab12cd34.txt are skipped. Note that the numeric part is limited to nine decimal figures.
If the limit of nine digits is disturbing, the following script can be used. It is very similar to the aforementioned one, but a numeric comparison has been replaced by a string comparison with the numbers padded by leading zeroes to have equal lengths. Therefore the string comparison provides the same result as a pure numeric comparison:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
set /A "DIGS=256"
setlocal EnableDelayedExpansion
for /L %%E in (1,1,%DIGS%) do set "PADZ=!PADZ!0"
endlocal & set "PADZ=%PADZ%"
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
set "CMPN=%PADZ%!NAME!"
set "CMPF=%PADZ%!FNEW!"
if "!CMPN:~-%DIGS%!" GTR "!CMPF:~-%DIGS%!" (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
This is a robust and more flexible approach, which allows to specify what numeric part to extract by its (zero-based) index, in the variable INDEX (a negative value counts from the back, so -1 points to the last one, if you prefer that):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "PREFIX=blah" & rem // (optional prefix to be used for the new file names)
set /A "INDEX=0" & rem // (`0` means first numeric part, `-1` means last one)
rem // Loop through command line arguments:
for %%F in (%*) do (
set /A "CNT=-1" & set "KIND="
for /F "delims== eol=" %%E in ('2^> nul set "$PART["') do set "%%E="
rem // Store information about currently iterated file:
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Assemble a list of file name portions of numeric and non-numeric parts:
set "LIST= "!FILE!" "
for /L %%J in (0,1,9) do set "LIST=!LIST:%%J=" %%J "!"
set "LIST=!LIST: "" =!"
rem // Determine file name portions, together with their count and kinds:
for %%I in (!LIST!) do (
endlocal & set /A "CNT+=1"
set "ITEM=%%~I" & set "TEST=%%I"
setlocal EnableDelayedExpansion
if "!TEST!"=="!ITEM!" (set "KND=0") else (set "KND=-")
for /F %%K in ("KIND=!KIND!!KND!") do (
for /F "delims=" %%E in ("$PART[!CNT!]=!ITEM!") do (
endlocal & set "%%K" & set "%%E"
)
)
setlocal EnableDelayedExpansion
)
rem // Retrieve the desired numeric file name portion:
if %INDEX% lss 0 (set /A "INDEX=-(1+INDEX)")
if %INDEX% lss 0 (set "RANGE=!CNT!,-1,0") else (set "RANGE=0,1,!CNT!")
set /A "IDX=-1" & set "FNEW=" & for /L %%J in (!RANGE!) do (
if "!KIND:~%%J,1!"=="0" set /A "IDX+=1" & (
if !IDX! equ !INDEX! for %%I in (!IDX!) do set "FNEW=!$PART[%%J]!"
)
)
rem // Actually rename file:
if defined FNEW (
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
)
endlocal
)
endlocal
exit /B

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.

Loop through "array" in batch file to shift elements

I have a batch file that is passed commands in the form of a string array from a Java file. The commands contain something like the following:
String[] commands = {"A",
"B",
"C",
"C:\users\user\Documents",
"C:\users\user\Pictures"}
The commands array is dynamic, as it changes every time the java program is run. In the batch file, I create variables to take the values of the first three elements (A, B, and C in this case). Then I need to shift the directory strings to take up the first three elements of the array. Here is the batch code I have so far:
#echo off
setlocal enableDelayedExpansion
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
set list[!old!]=!list[%new%]!
echo !list[%old%]!
set /A old=!old!+1
set /A new=!new!+1 )
The problem I am having is with the line set list[!old!]=!list[%new%]!. As you can see, I have delayed expansion enabled. However, the !!'s are needed for the list[...] variable that is emulating an element in an array. However, I believe I need to use delayed expansion for "new" as well. What am I to do in this case? Or perhaps that's not the actual problem? The "old" and "new" variables are incrementing correctly, but the echo !list[%old%]! line returns the same value every time. I expect the same issue exists in that line, with "old"--It should have !'s surrounding it but the !'s are already being used for the list[...] variable. So what happens if you need nested !'s in a statement? Thanks for the aid!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=-3
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[-3]%
set argB=%list[-2]%
set argC=%list[-1]%
for /F "tokens=2 delims==" %%a in ('set list[-') do SET "%%a="
SET arg
SET list
ENDLOCAL
echo==================
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
CALL set list[%%old%%]=%%list[!new!]%%
CALL ECHO(%%list[!old!]%%
set /A old=!old!+1
set /A new=!new!+1
)
SET arg
SET list
GOTO :EOF
This should work for you - the easy way and the hard way.

Resources