Windows Batch to read file and parse lines into tokens and variables - windows

I've made a good deal of headway by searching this site and learning the ridiculous language that is Windows batch scripting, but I'm now at a point where I'm stuck. I have a text file with a variable number of lines, each of which looks something like:
AA8315,"United States",N777AN,"American Airlines",AAL98,B772,"Boeing 777-223",AAL,"2013-06-11 23:30:47.923","2013-06-12 00:01:14.459"
My batch file:
set THEDATE=2013-06-12
set THEDATABASE=c:\Kinetic\BaseStation\Basestation.sqb
set THECSVFILE=c:\Flights.csv
set THEOUTPUTFILE=c:\FlightsNew.csv
set THISLINE=""
if exist %THECSVFILE% del %THECSVFILE%
if exist %THEOUTPUTFILE% del %THEOUTPUTFILE%
:: allow time for the csv file to be deleted
timeout /t 2 /nobreak
c:\sqlite3.exe -header -csv %THEDATABASE% "select Aircraft.ModeS, Aircraft.ModeSCountry as Country, Aircraft.Registration as Reg, Aircraft.RegisteredOwners as Owner, Flights.Callsign, Aircraft.ICAOTypeCode as Type, Aircraft.Type as Model, Aircraft.OperatorFlagCode as 'Op Flag', Flights.StartTime as 'First Seen', Flights.EndTime as 'Last Seen' from Aircraft INNER JOIN Flights ON (Aircraft.AircraftID=Flights.AircraftID) where Flights.EndTime like '%THEDATE% %%' order by Flights.EndTime DESC;" >> %THECSVFILE%
::allow time for the csv to be written to file
timeout /t 5 /nobreak
::read %THECSVFILE% and loop through each line
for /F "usebackq tokens=* delims=" %%A in (%THECSVFILE%) do (
set the_line=%%A
call :process_line
)
:process_line
for /F "usebackq tokens=1,2,3,4,5,6,7,8,9,10 delims=[,]" %%1 in (%the_line%) do (
set hexcode=%%1
set country=%%2
set reg=%%3
set owner=%%4
set callsign=%%5
set planetype=%%6
set model=%%7
set opflag=%%8
set firstseen=%%9
set lastseen=%%10
set THISLINE=%hexcode%,%country%,%reg%,%owner%,%callsign%,%planetype%,%model%,%opflag%,%firstseen%,%lastseen%
echo %THISLINE% > %THEOUTPUTFILE%
)
(I'm assigning the tokens to variables because I will be doing additional validation and formatting of them later. I need to get this part working first!)
When executed, the script does indeed loop through each line of the file, however it does not seem to be assigning %%1 to the variable hexcode.
The output of the executed command looks like this:
C:\>for /F "usebackq tokens=1,2,3,4,5,6,7,8,9,10 delims=[,]" %1 in (AA8315 "United States" N777AN "American Airlines" AAL98 B772 "Boeing 777-223" AAL "2013-06-11 23:30:47.923" "2013-06-12 00:01:14.459") do (
set hexcode=%1
set country=%2
set reg=%3
set owner=%4
set callsign=%5
set planetype=%6
set model=%7
set opflag=%8
set firstseen=%9
set lastseen=%10
set THISLINE=,"United States" ,N807FD ,"Fedex Express" ,FDX1378 ,,"Airbus A310-324" ,FDX ,"2013-06-12 22:56:54.639" ,"2013-06-12 23:05:31.822"
echo "" 1>c:\FlightsNew.csv
)
The system cannot find the file AA8315.
Any help is greatly appreciated!

this works here:
for /f "tokens=1-10delims=," %%a in ("AA8315,"United States",N777AN,"American Airlines",AAL98,B772,"Boeing 777-223",AAL,"2013-06-11 23:30:47.923","2013-06-12 00:01:14.459"") do (
set hexcode=%%a
set country=%%b
set reg=%%c
set owner=%%d
set callsegn=%%e
set planefype=%%f
set model=%%g
set opflag=%%h
set firstseen=%%i
set lastseen=%%j
set THISLINE=%%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,%%i,%%j
)
>"c:\FlightsNew.csv" echo %THISLINE%
I'm not sure, why you need the tokens.

I have always had problems with comma separated values in a for loop. Here's what I did to make your code work.
Test.txt
AA8315,"United States",N777AN,"American Airlines",AAL98,B772,"Boeing 777-223",AAL,"2013-06-11 23:30:47.923","2013-06-12 00:01:14.459"
BatchFile.bat
set THECSVFILE=test.txt
::read %THECSVFILE% and loop through each line
for /F "usebackq tokens=* delims=" %%A in (%THECSVFILE%) do (
set the_line=%%A
call :process_line
)
goto TheEnd
:process_line
for /F "usebackq tokens=1,2,3,4,5,6,7,8,9,10 delims=~" %%1 in ('%the_line:,=~%') do (
set hexcode=%%1
set country=%%2
set reg=%%3
set owner=%%4
set callsign=%%5
set planetype=%%6
set model=%%7
set opflag=%%8
set firstseen=%%9
set lastseen=%%10
set THISLINE=%hexcode%,%country%,%reg%,%owner%,%callsign%,%planetype%,%model%,%opflag%,%firstseen%,%lastseen%
echo %THISLINE% > %THEOUTPUTFILE%
)
:TheEnd
Notice the :process_line for loop. I had to add single quotes around the %the_line% so it didn't try to interpret the string as a filename. Then I replaced all commas with the ~ character, and used the ~ character as the delimiter. It may not work precisely with all your data (if it contains single quotes or the ~ character), but it does work with this one record and gets you moving in the right direction again.

You can only use letters fro the metavariable (%%1 in your code) - but the lower-case and upper-case letters are distinct.
Yes, you can use some other characters, but the contiguous blocks avaliable for "tokens=1-10" (which is an easier version of 1,2,3...) are a..z and A..Z
%0..%9 are reserved for the parameters to the batch or batch-procedure.

Related

Windows Batch - Find word in one string matching word in another string and capture output

While this may seem easy to some, I've struggled for hours on it.
I have a file:
MYFOLDER,JobE,JobD_ENDED_OK,
MYFOLDER,JobD,JobC_ENDED_OK,JobD_ENDED_OK
MYFOLDER,JobD,JobB_ENDED_OK,
MYFOLDER,JobC,JobA_ENDED_OK,JobC_ENDED_OK
MYFOLDER,JobB,JobA_ENDED_OK,JobB_ENDED_OK
MYFOLDER,JobA,,JobA_ENDED_OK
I need to loop through and find where token 4 in one line matches token 3 in another line and then echo a statement to a file. I am looking for an output file that shows this:
MYFOLDER_JobA_MYFOLDER_JobB matches JobA_ENDED_OK
MYFOLDER_JobA_MYFOLDER_JobC matches JobA_ENDED_OK
MYFOLDER_JobB_MYFOLDER_JobD matches JobB_ENDED_OK
MYFOLDER_JobC_MYFOLDER_JobD matches JobC_ENDED_OK
MYFOLDER_JobD_MYFOLDER_JobE matches JobD_ENDED_OK
I know it's a FOR loop with a DO, I am just not getting the rest of it.
Any assistance is greatly appreciated.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q46510665.txt"
SET "outfile=%destdir%\outfile.txt"
(
FOR /f "usebackqdelims=" %%h IN ("%filename1%") DO (
SET "col4line=%%h"
SET "col4line=!col4line:,=|,|!"
FOR /f "tokens=1-4delims=," %%a IN ("!col4line!") DO IF "%%d" neq "|" (
FOR /f "usebackqdelims=" %%H IN ("%filename1%") DO (
SET "col3line=%%H"
SET "col3line=!col3line:,=|,|!"
FOR /f "tokens=1-4delims=," %%A IN ("!col3line!") DO (
IF "%%d|"=="%%C" (
SET "reportline=%%a_%%b_%%A_%%B matches %%C"
ECHO !reportline:^|=!
)
)
)
)
)
)>"%outfile%"
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q46510665.txt containing your data for my testing.
Produces the file defined as %outfile%
For each line in the file, set a variable col4line to the entire line via %%h, then replace each , with |,| so that successive , will be separated. Tokenise on , and ignore any line which has simply | as its 4th token (ie last-column-empty).
Repeat the process for every line in the file this time through %%H into col3line (note case differential to use different actual metavariables) and if the third column matches the fourth column+| from the outer loop, assemble the report line from the tokens and output, removing the |s.

Last token in batch delimited by minus

I have a string "xx-yy-zz"
I need to get the part after the last minus character.
Usually, I do it that way
FOR /F "tokens=1-3 delims=-" %%i IN ("xx-yy-zz") DO SET USER_COUNTRY=%%k
echo Country: %USER_COUNTRY%
But now I don't have the string with a fixed length (there could be 2/3/4 delimiters)
How can I specify that I want the last one? So I have the code that would work for "xx-yy-zz" and also for "xx-yy-zz-aa"
Could someone help with this, please?
use a plain for (without /f):
set string=xxx-yyy-zzz
for %%a in (%string:-= %) do set last=%%a
echo %last%
(%string:-= % replaces every - with a space, so the for can split the string into tokens and assign every one of them to (the same) variable, resulting in the variable holding the last token.
The option string tokens=1-4 would let the for /F loop assign the last string portion aa to variable reference %%l if it is present; %%l would be empty otherwise. So you could do an if query to check for emptyness and use %%k instead in case, and so on, like this:
for /F "tokens=1-4 delims=-" %%i in ("xx-yy-zz-aa") do (
set "USER_COUNTRY=%%l"
if not defined USER_COUNTRY set "USER_COUNTRY=%%k"
if not defined USER_COUNTRY set "USER_COUNTRY=%%j"
)
echo/%USER_COUNTRY%
An alternative method would be this:
set "STRING=xx-yy-zz-aa"
set "DUMMY=%STRING:-=" & set "USER_COUNTRY=%"
echo/%USER_COUNTRY%
This approach might fail if the input string contains " characters.
One solution could be to convert the string, replacing dashes with backslashes so it "seems" a path that can be handled with a for command
#echo off
setlocal enableextensions disabledelayedexpansion
set "string=xxx-yyy-zzz"
for /f "delims=" %%a in ("::%string:-=\%") do set "last=%%~nxa"
echo %last%
An additional example:
#Echo Off
Set/P "InString=Enter your delimited string: "
Echo=
If "%InString%"=="" GoTo :EOF
Set/P "Delimiter=Enter the delimiter: "
Echo=
If "%Delimiter%"=="" GoTo :EOF
Set "Delimiter=%Delimiter:~,1%"
:Loop
If Defined InString (
For /F "EOL=%Delimiter% Tokens=1* Delims=%Delimiter%" %%A In ("%InString%"
) Do (Set "LastToken=%%A" & Set "InString=%%B") & GoTo Loop
) Else Set LastToken
Timeout -1
Exit/B
This version will still have issues with some characters

Joining a list of numbers in a .txt file with ,[space]

Hi I managed to get the code below to create a list of numbers and place a comma at the end of each number created However, it has proven to be quite the challenge to get them on the same line separated by a ,[space]
#ECHO OFF
setlocal EnableDelayedExpansion
set _Output=%UserProfile%\Desktop\NumGen.txt
::only change these three lines
set "start=1" ::starts from this number
set "amount=10" ::amount of files created
set "length=5" ::length of fileNames
set "join_with=, " ::what to join each number with
set /a "last=%start%+%amount%"
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "folderName=!folderName:~-%length%!%join_with%"
>>"%_Output%" ECHO.!folderName!
)
pause
so my output at the moment is
00001,
00002,
00003,
00004,
00005,
00006,
00007,
00008,
00009,
00010,
00011,
However I would like it to be
00001, 00002, 00003, 00004, 00005, 00006, 00007, 00008, 00009, 00010, 00011
I have windows 10 64bit. Any help will be appreciated
echo is not able to do that. But there is a workaround, (ab)using the set command:
for /l %%i in (1,1,5) do (
<nul set /p "=%%i, "
)
echo(
After a long break I decided to give this another bash and presto. Success :-)
::CallScript
#ECHO OFF
CALL :ScriptA
CALL :ScriptB
CALL :ScriptC
pause
goto :eof
:ScriptA
#ECHO OFF
setlocal EnableDelayedExpansion
set _Output=%UserProfile%\Desktop\NumGen.txt
::only change these three lines
set "start=1"
set "amount=10"
set "length=5"
set "join_with=, "
set /a "last=%start%+%amount%"
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "folderName=!folderName:~-%length%!%join_with%"
>>"%_Output%" ECHO.!folderName!
)
goto :eof
:ScriptB
#ECHO OFF
setlocal enableextensions enabledelayedexpansion
set "var="
for /f "usebackq delims=" %%a in ("%UserProfile%\Desktop\NumGen.txt") do set "var=!var!%%a"
echo %var%> "%UserProfile%\Desktop\NumList.txt"
goto :eof
:ScriptC
#ECHO OFF
del "%UserProfile%\Desktop\NumGen.txt"
goto :eof
This code determines the starting number, the amount of numbers, the length of the numbers and joins them with , "
Then ScriptB concatenates each number together with the , " and saves to NumList.txt
Lastly the script deletes the NumGen.txt file.
Stephan has one solution with SET /P. The other option is to build the entire string within an environment variable, and then write after the loop ends. This is significantly faster, but it will fail if the final string length exceeds ~8191 bytes.
set "str="
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "str=!str!!folderName:~-%length%!%join_with%"
)
>>"%_Output%" ECHO.!str!
You could remove the unwanted trailing , if you want:
>>"%_Output%" ECHO.!str:~0,-2!

Reading from a csv file and extracting certain data columns based on first column value

This is my first batch program and I have been searching online but still struggling to write up a solution.
I have the following CSV file:
"RH",2013/06/15 02:14:58 -0400,"X","LQ3SUEEWPWKL6",005,
"FH",01
"SH",2013/06/14 00:00:00 -0400,2013/06/14 23:59:59 -0400,"LQ3SUEEWPWKL6",""
"CH","TransactionID","InvoiceID",
......
I'm trying to write a simple program to do the following:
If column1 = "RH", then extract column2 value (2013/06/15 02:14:58 -0400)
If column1 = "SH", then extract column4 value (LQ3SUEEWPWKL6)
and pipe output to a file.
This is my code so far but the if condition is not working for me
#echo off
:: Set input file in variable
::Set _InputFile=%1
:: Store input line into different variables
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
Set _var1=%%A
Set _var2=%%B
Set _var3=%%C
Set _var4=%%D
Set _var5=%%E
Set _var6=%%F
Set _var7=%%G
Set _var8=%%H
Set _var9=%%I
Set _var10=%%J
Set _var11=%%K
Set _var12=%%L
Set _var13=%%M
Set _var14=%%N
Set _var15=%%O
Set _var16=%%P
Set _var17=%%Q
Set _var18=%%R
IF "%_var1%"=="RH" echo %var2%
)
My CSV file looks fine in Excel and Notepad but when I execute the script to display the first variable, it looks like there's some garbage characters just before the "RH" on the first record - I cannot bypass it since I need to extract additional column data if var1 = "RH":
"RH"
FH
01
SH
CH
TransactionID,PaymentTrackingID,
SF
SF
SC
RF
CAD,CR,0
RF
USD,CR,0
RC
FF
(
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
if "%%~A"=="RH" echo %%~B
if "%%~A"=="SH" echo %%~D
)
)>youroutputfilename
Should work - no need to assign all the values to different variables - BUT if you plan to use them, then
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
...
Set _var17=%%Q
Set _var18=%%R
CALL :PROCESS
)
...
GOTO :EOF
:PROCESS
IF %_var1%=="RH" echo %_var2%
IF %_var1%=="SH" echo %_var4%
GOTO :EOF
Note that with this method, since you are assigning %%x to _varx then if %%x is quoted, the quotes will be INCLUDED in the value assigned. To remove the enclosing quotes (if they exist) use SET _varx=%%~x.
Addendum 20130703-1956Z for OP's problem
#ECHO OFF
SETLOCAL
SET _Inputfile=u:\noname1.txt
(
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
SET "RH="
SET "SH="
ECHO(%%A|FINDSTR /l /c:"\"RH\"" >NUL
IF NOT ERRORLEVEL 1 SET RH=Y
ECHO(%%A|FINDSTR /l /c:"\"SH\"" >NUL
IF NOT ERRORLEVEL 1 SET SH=Y
if DEFINED RH echo %%~B
if DEFINED SH echo %%~D
)
)>u:\youroutputfilename
TYPE u:\youroutputfilename
del u:\youroutputfilename
echo========First way
(
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
SET _var1=%%A
SET "RH="
SET "SH="
CALL :process
if DEFINED RH echo %%~B
if DEFINED SH echo %%~D
)
)>u:\youroutputfilename
TYPE u:\youroutputfilename
del u:\youroutputfilename
echo========Second way
SETLOCAL ENABLEDELAYEDEXPANSION
(
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
SET _var1=%%A
IF "!_var1:~-4!"==""RH"" echo %%~B
IF "!_var1:~-4!"==""SH"" echo %%~D
)
)>u:\youroutputfilename
TYPE u:\youroutputfilename
del u:\youroutputfilename
echo========Third way
ENDLOCAL
GOTO :EOF
:process
IF "%_var1:~-4%"==""RH"" SET RH=Y
IF "%_var1:~-4%"==""SH"" SET SH=Y
GOTO :EOF
You have a parsing issue. First end the for loop with ), after this you can use the new variables:
#echo off
:: Set input file in variable
::Set _InputFile=%1
:: Store input line into different variables
FOR /F "tokens=1-18* delims=," %%A IN (%_InputFile%) DO (
Set "_var1=%%A"
Set "_var2=%%B"
Set "_var3=%%C"
Set "_var4=%%D"
Set "_var5=%%E"
Set "_var6=%%F"
Set "_var7=%%G"
Set "_var8=%%H"
Set "_var9=%%I"
Set "_var10=%%J"
Set "_var11=%%K"
Set "_var12=%%L"
Set "_var13=%%M"
Set "_var14=%%N"
Set "_var15=%%O"
Set "_var16=%%P"
Set "_var17=%%Q"
Set "_var18=%%R"
)
IF "%_var1%"=="RH" echo %var2%
You need to enable delayed expansion:
#echo off
setlocal EnableDelayedExpansion
set "_InputFile=..."
for /f "tokens=1-18* delims=," %%A in (%_InputFile%) do (
Set _var1=%%A
Set _var2=%%B
...
if "!_var1!"=="RH" echo !_var2!
)
as there was no answer to the "why does my line starts with "RH"", I'll do some gravedigging.
So, the  comes from the BOM (Byte Order Mark) which indicates the file is in UTF, and the way the bytes are written if necessary.
for the answer:
you can use
if x%_var1:RH=%x NEQ x%_var1%x (echo %_var2%)
this will check if RH is in %_var1% (if after replacing RH in the var, it is unchanged, RH is not in the var)
which means, whether the Bom is here or not is not important. Though, you'll have problems if you want an exact match.
another way to deal with it is to not include the bom in your file, which means saving either in ASCII or UTF-8 without BOM; Or using a tool to strip the bom from your UTF-8 file.

Help in writing a batch script to parse CSV file and output a text file

I am struggling to write a batch script which can read a CSV file such as below
Name:, City:, Country:
Mark, London, UK
Ben, Paris, France
Tom, Athens, Greece
There will be a heading row in the CSV file. It should output to a text file as below:
Name:Mark
City:London
Country:UK
Name:Ben
City:Paris
Country:France
Name:Tom
City:Athens
Country:Greece
The field separator (:) in the above output is expected to be provided in the header row itself. So all that I need to do is concatenate the field heading and its value.
The number of columns in this CSV file is not fixed, so the script should not limit to 3 tokens. Kindly help!
#ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-10 delims=," %%a IN ("%filename%") DO ^
CALL :process "%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"
GOTO :EOF
:trim
SET "tmp=%~1"
:trimlead
IF NOT "%tmp:~0,1%"==" " GOTO :EOF
SET "tmp=%tmp:~1%"
GOTO trimlead
:process
SET /A linenum+=1
IF "%linenum%"=="1" GOTO picknames
SET ind=0
:display
IF "%fcount%"=="%ind%" (ECHO.&GOTO :EOF)
SET /A ind+=1
CALL :trim %1
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !f%ind%!!tmp!
ENDLOCAL
SHIFT
GOTO display
:picknames
IF %1=="" GOTO :EOF
CALL :trim %1
SET /a fcount+=1
SET "f%fcount%=%tmp%"
SHIFT
GOTO picknames
This batch scipt:
accepts one parameter, the name of the file to process;
does not verify the presence of : at the end of a header token, and when the values are displayed they are placed immediately after the corresponding header tokens;
trims all the leading spaces (but not the trailing ones);
considers the first row to be the header row, which also defines the number of tokens to process in subsequent rows;
supports up to 10 tokens, and the two areas highlighted in bold italics are responsible for that (so when you need to change the maximum number, modify both areas: if you increase the number, you must expand the "%%a" "%%b" "%%c" … list, and, likewise, if you decrease the number, then shrink the list).
I know this is an old question, but this type of question is my favorite one so here it is my answer:
#echo off
setlocal EnableDelayedExpansion
rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
set /A i+=1
set heading[!i!]=%%~h
)
rem Process the file:
call :ProcessFile < %1
exit /B
:ProcessFile
set /P line=
:nextLine
set line=:EOF
set /P line=
if "!line!" == ":EOF" goto :EOF
set i=0
for %%e in (%line%) do (
set /A i+=1
for %%i in (!i!) do echo !heading[%%i]!%%~e
)
goto nextLine
exit /B
This program have not any limit in the number of fields. This version requires to enclose in quotes the elements that may have spaces or other Batch delimiters, but this restriction may be easily fixed.
Python makes this so easy it should be regulated by the government.
from csv import DictReader
with open('file', 'rb') as file:
reader = DictReader(file)
for line in reader:
for field in reader.fieldnames:
print '{0}{1}'.format(field.strip(), line[field].strip())
print '\n'
Edit: I guess you need something native to the Windows command shell. Oh well.

Resources