Windows Batch read a file, parse and output - batch-file

I'm 90% of the way there on a Windows Batch file.
It takes 2 input parameters, input and output files.
It then reads in the input file, and substrings certain lines into arrays (Well line 2 onwards).
Then we come to a loop for outputting.
With delayed expansion on my counter for going through the array doesn't update unless I use !counter2!, %counter2% doesn't work.
Using !arrayname[!counter2!]! doesn't work.
Here is the code as it stands.
#Echo off
if [%1] == [] goto usage
if [%2] == [] goto usage
echo start time : %time%>logfile.log
set input_file=%1
set output_file=%2
if exist %output_file% del %output_file%
Echo Start reading %input_file%>> logfile.log
setLocal EnableDelayedExpansion
set /a counter=1
for /F "tokens=* delims=" %%a in ('type %input_file%') DO (
::echo !counter!
if "!counter!"=="1" set header=%%a
if not "!counter!"=="1" (
set data[!counter!]=%%a
set line=%%a
set jobnumber[!counter!]=!line:~0,7!
set docnumber[!counter!]=!line:~7,5!
set pagecount[!counter!]=!line:~12,2!
set customernumber[!counter!]=!line:~14,20!
set presort[!counter!]=0000
set postcode[!counter!]=0000
set inserts[!counter!]=!line:~36,11!
set filler[!counter!]=000000
set address[!counter!]=!line:~58,350!
set filler2[!counter!]=" "
set endline[!counter!]=X
)
set /a counter=counter+1
)
Echo Start writing %output_file%>> logfile.log
for /L %%G in (2,1,%counter%) DO (
set counter2=%%G
echo !counter2!
echo !jobnumber[%counter2%]!!docnumber[%counter2%]!!pagecount[%counter2%]!!customernumber[%counter2%]!!presort[%counter2%]!!postcode[%counter2%]!!inserts[%counter2%]!!filler[%counter2%]!!address[%counter2%]!!filler2[%counter2%]!!endline[%counter2%]!>>%output_file%
)
echo end time : %time%>>logfile.log
pause
goto :eof
:usage
echo Usage: blah.bat input_filename output_filename
pause
goto :eof
It is the echo !jobnumber[%counter2%]! where things are not being resolved.
The echo !counter2! works fine.
Before you ask, Yes I know this could be done better and easier in C# or another programming language, However I am tasked with doing it in a windows batch file.
Thanks in advance for any help provided.
Tel

Try with:
for /L %%G in (2,1,%counter%) DO (
set counter2=%%G
echo !counter2!
echo !jobnumber[%%G]!!docnumber[%%G]!!pagecount[%%G]!!customernumber[%%G]!!presort[%%G]!!postcode[%%G]!!inserts[%%G]!!filler[%%G]!!address[%%G]!!filler2[%%G]!!endline[%%G]!>>%output_file%
)
You are not changing the value of the coutner2 so you don't need it and you can directly use %%G.
Though if you need changes in counter2 you'll have to wrap it again in for loop and to use its tokens.

Related

If a variable gets set to the name of a variable, then can it become that variable? (Batch)

All the P(numbers) are paths. For example, %P1% might be D:\ . Then, I get them to say a number. If they choose 1, I combine that with a P. That gives P1, the first variable. Can I set A to the variable P1? It would make the code way more optimized. I want to try and fit everything in a ridiculously tiny amount of space.
Download: https://mega.nz/file/QYZA3RbQ#Uvr9acXEBssYdXSHkdas1OjFt0X6xdmyl1WLH9HbmP8
echo 1. %P1%
echo 2. %P2%
echo 3. %P3%
echo 4. %P4%
echo 5. %P5%
set /p Pc=
set "a=p%pc%"
echo %a%
pause```
Short answer, based on the initial code:
Using delayedexpansion
set "a=!P%Pc%!"
echo %a%
or use call
call set "a=%%P%Pc%%%"
echo %a%
However. to do this right, You will need to change the way your config file works.
For now, your config has a major flaw, you explicitly want to ignore certain line numbers, which if someone removes or add a line, it will break your code. You need to use some for loops and then skip lines that starts with REM
So the config needs to look something like this (I made it smaller for the sake of a shorter answer):
REM Please set this to 1, or the program will NOT run!
1
REM ------------------------------------------------------------------------------------------
REM Please put paths to the folder your [PWADS] are in. Not the pwads themselves.
REM ------------------------------------------------------------------------------------------
pwads=C:\Some path
pwads=D:\Another
REM ------------------------------------------------------------------------------------------
REM Please put the path to your [IWADS]. Only ten though! Make sure it's the iwads themselves.
REM ------------------------------------------------------------------------------------------
iwads=D:\iwads1
iwads=D:\some path2
REM ------------------------------------------------------------------------------------------
REM Please put the path to your [Source Port] exe's. You must be doing something weird if you have more than ten.
REM ------------------------------------------------------------------------------------------
sp=D:\
sp=E:\
REM ------------------------------------------------------------------------------------------
REM Now, please leave this alone. It's so I know everything works.
REM ------------------------------------------------------------------------------------------
1
Now the code. portion.
#echo off
setlocal enabledelayedexpansion
set pwnum=0
set iwnum=0
set spnum=0
for /f "tokens=1,* delims==" %%i in ('type launcherconfiguration.txt ^| findstr /v "REM "') do (
if "%%i" equ "0" (
echo not configured
goto :eof
)
if "%%~i" == "pwads" (
set /a pwnum+=1
set "PWADlocat!pwnum!=%%~j"
) else (
if "%%~i" == "iwads" (
set /a iwnum+=1
set "IWADlocat!iwnum!=%%~j"
) else (
if "%%~i" == "sp" (
set /a spnum+=1
set "SPlocat!spnum!=%%~j"
)
)
)
)
echo PWADLocation:
for /l %%a in (1,1,%pwnum%) do echo %%a. !PWADLocat%%a!
set /p pw=Choose:
call echo you chose %%PWADlocat!pw!%%
call set "PW=%%PWADlocat!sp!%%"
echo you chose %PW%
pushd "%PW%" rem This will goto the dir
rem do other things
popd
echo IWADLocation:
for /l %%a in (1,1,%iwnum%) do echo %%a. !IWADLocat%%a!
set /p pw=Choose:
call set "IW=%%IWADlocat!sp!%%"
echo you chose %IW%
pushd "%IW%" rem This will goto chosen the dir
rem do other things
popd
echo SPLocation:
for /l %%a in (1,1,%spnum%) do echo %%a. !SPLocat%%a!
set /p sp=Choose:
call set "SP=%%SPlocat!sp!%%"
echo you chose %SP%
pushd "%SP%" rem This will goto the chosen SP dir
rem do other things
popd
So let me explain it briefly. We set a number per datatype, we simply increase the count as more lines matched are found and then we use another for /l loop to just use the count again.
To test this, you need to copy this code into a new batch file, place it in the same location as your config file and test it. There are alot more to your code, but I have only focused on what you asked.
Note, I would still prefer to use choice but because of the beeping you mentioned, I retained the set /p option in this code.

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

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.

Batch script not working

I've recently written a password script in batch. Basically it takes the password from a different batch script, finds out how many characters it has, takes the password from the user and compares them. However, nothing is ever simple with me and I've gotten into some nested FOR loops and such. It's hard to explain, so here's the code:
#echo off
setlocal enabledelayedexpansion
call pass.bat & rem Sets the variable "pass" as "default".
set map=abcdefghijklmnopqrstuvwxyz
set len=0
for /l %%A in (12,-1,0) do (
set /a "len|=1<<%%A"
for %%B in (!len!) do if "!pass:~%%B,1!"=="" set /a "len&=~1<<%%A"
)
for /l %%C in (1,1,100) do (
set letter%%C=!pass:~%%C,1!
)
:pass
for /l %%D in (0,1,%len%) do (
cls
choice /c "abcdefghijklmnopqrstuvwxyz" /n /m "Password: !ast!"
set /a charerr=!errorlevel!-1
for /l %%E in (0,1,25) do (
set password=!password!%map:~!charerr!,1% & rem This is the problem line.
)
set ast=!ast!*
)
if "%pass%" neq "%password%" goto fail
cls
echo Correct Password
echo pass: %pass%
echo password: %password%
>nul pause
exit
:fail
set /a tries-=1
if %tries% geq 1 goto pass
Now, the script doesn't crash or anything like that, however it does not set password as the password you entered.
If you run it you'll understand.
Note: pass.bat purely contains the line set pass=default
You could try
call set "password=!password!%%map:~!charerr!,1%%"
Your variant can't work, as percent expansions are expanded when a block is parsed, so the %map:~!charerr!,1% will be expanded, but fails as !charerr! isn't expanded at that time.
The CALL can start a second parse time when evaluating the line and the double percent will then expand correct.

redirect standard output to both screen and text file

I'm writing a dos batch script and one thing I want to do is to redirect a command line out to both standard output and a text file. I can do either, but not both. Also, I can't call it with another batch script or call it at command line.
the command I want to execute is reg add "%regKey%" /f
and the output is The operation completed sucessfully. and I want it to display on standard output and append in the text file.
So far, I have tried:
reg add "%regKey%" /f>>%logfile%>CON <-- only appended to textfile
reg add "%regKey%" /f>CON>>%logfile% <-- only displayed in standard output
I saw this SO, but it's for bash.
reg add "%regKey%" /f>>%logfile%
type %logfile%
Which is easier to understand for yourself, and anyone else that will come along looking at your script.
I solved it. Here is the magic, although the performance isn't that great and it wouldn't work for certain things:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set logfile=test.out
echo. 2>%logfile%
set /A count=0
set /A lastline=0
:loop
if not %count%==100 (
set /A count += 1
SET PR >> %logfile%
CALL :printlog
goto :loop
)
goto :pause
:printlog
SET incount=0
FOR /F "delims=" %%M in (%logfile%) DO (
set /A incount +=1
if %incount% GEQ %lastline% (
ECHO %%M
SET /A lastline=%incount%
)
)
ECHO -
EXIT /B 0
:pause
pause

Resources