Convert multi-record text file to CSV in a Windows Batch File - batch-file

I have tried several approaches to this and had some success, but now I am trying to stitch it all together and need a little assistance.
The following text file shows three records separated by a blank line. The exact number of records is variable but unlikely to be more than 30.
ITEM 1 SONG
TITLE Blow My Mind (feat. Chris Brown)
ARTISTS Davido
START 00:00:33.6900000
END 00:03:19.2700000
ITEM 2 SONG
TITLE So Good
ARTISTS Alex Blue
START 00:03:19.2700000
END 00:06:01.2330000
ITEM 3 SONG
TITLE Easy To Love (feat. Teddy Swims)
ARTISTS Armin van Buuren and Matoma
START 00:06:07.1430000
END 00:08:37.4520000
Using the following script, I have been able to extract the data and remove the field names:
for /F "tokens=1* delims= " %%A in (text_record.txt) do (
if "%%A" == "ITEM" echo %%B
if "%%A" == "TITLE" echo %%B
if "%%A" == "ARTISTS" echo %%B
if "%%A" == "START" echo %%B
if "%%A" == "END" echo %%B
)
This provides the following result:
1 SONG
Blow My Mind (feat. Chris Brown)
Davido
00:00:33.6900000
00:03:19.2700000
2 SONG
So Good
Alex Blue
00:03:19.2700000
00:06:01.2330000
3 SONG
Easy To Love (feat. Teddy Swims)
Armin van Buuren and Matoma
00:06:07.1430000
00:08:37.4520000
Also, using the following script, I have been able to concatenate first record into a single line CSV:
< text_record.txt (
set /p "line1="
set /p "line2="
set /p "line3="
set /p "line4="
set /p "line5="
set /p "line6="
)
set "var=%line1%,%line2%,%line3%,%line4%,%line5%"
echo.
echo("%var%"
This provides the following result:
“ITEM 1 SONG,TITLE Blow My Mind (feat. Chris Brown),ARTISTS Davido,START 00:00:33.6900000,END 00:03:19.2700000"
However, what I want is for each record to be on a separate line, with the field names removed, for example:
1 SONG,Blow My Mind (feat. Chris Brown),Davido,00:00:33.6900000,00:03:19.2700000
2 SONG,So Good,Alex Blue,00:03:19.2700000,00:06:01.2330000
3 SONG,Easy To Love (feat. Teddy Swims),Armin van Buuren and Matoma,00:06:07.1430000,00:08:37.4520000
I think I am close but I need some assistance to pull it together.

Try this:
#echo off
setlocal EnableDelayedExpansion
set "output="
for /F "tokens=1,2* delims=: " %%a in ('findstr /N "^" text_record.txt') do (
if "%%b" neq "" (
set "output=!output!,%%c"
) else (
echo !output:~1!
set "output="
)
)
if defined output echo !output:~1!

Related

Reformatting Web Query Results for use in Batch

This is working, but doesn't feel elegant to me. I'm creating an automated movie archive script in batch and would like to automatically find a movie title based on the disc volume name. The web query is done via tmdb, but returned results is difficult to parse since it isn't meant for batch. The results would be a contiguous line like:
{"page":1,"results":[{"poster_path":"\/5ttOaThDVmTpV8iragbrhdfxEep.jpg","adult":false,"overview":"At the height of the Cold War, a mysterious criminal organization plans to use nuclear weapons and technology to upset the fragile balance of power between the United States and Soviet Union. CIA agent Napoleon Solo and KGB agent Illya Kuryakin are forced to put aside their hostilities and work together to stop the evildoers in their tracks. The duo's only lead is the daughter of a missing German scientist, whom they must find soon to prevent a global catastrophe.","release_date":"2015-08-13","genre_ids":[35,28,12],"id":203801,"original_title":"The Man from U.N.C.L.E.","original_language":"en","title":"The Man from U.N.C.L.E.","backdrop_path":"\/bKxcCNv2xq8M3GD5iSrv9bMGDVa.jpg","popularity":5.346674,"vote_count":1842,"video":false,"vote_average":7},{"poster_path":"\/3VScfiBmE1loQxMkuN1suALv4f8.jpg","adult":false,"overview":"When THRUSH steals a nuclear weapon and demands a ransom delivered by Napoleon Solo, UNCLE recalls him and his partner to duty.","release_date":"1983-04-05","genre_ids":[28,80,53,10770],"id":94116,"original_title":"The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair","original_language":"en","title":"The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair","backdrop_path":"\/5LGBhGg5Tj9OSW4rD0itz0sYKPT.jpg","popularity":1.046707,"vote_count":5,"video":false,"vote_average":3.6}],"total_results":2,"total_pages":1}
You don't really know what you're going to get or how many titles will be returned. Dumping this into a file and reading back tokens doesn't make sense. The delimiter is a string (,") so I've come up with the following script which does function.
#echo off
setlocal EnableDelayedExpansion
set _tmdbReturn=
set _metaDataFile=
set _metaDataFile="C:\some path\metaData.txt"
set _metaDataFile=%_metaDataFile:~1,-1%
:: Do a movie title search based on a Disc Volume Label
for /f "usebackq delims=" %%a in (`PowerShell -Command "(new-object net.webclient).DownloadString('https://api.themoviedb.org/3/search/movie?api_key=xxx&query=The+Man+from+uncle')"`) do (set _tmdbReturn=%%a)
:: Result is in a contiguous string and the delimiter is a string with a comma and double quotes (,")
:: Replace delimiter string with a single character that does not occur in tmdb data
set _tmdbReturn=%_tmdbReturn:,"=#"%
set _tmdbReturn=%_tmdbReturn:"=%
:: replace unique single character with a line feed
set _tmdbReturn=!_tmdbReturn:#=^
!
:: Eliminate the special character
set _tmpdbReturn=!_tmdbReturn:#=!
:: Rewrite data to txt file with row separated data.
echo !_tmdbReturn!>"%_metaDataFile%"
set x=
set /a x=0
for /f "tokens=* delims=#" %%a in ('type "%_metaDataFile%"') do (
if !x!==0 (
set _newline=%%a
echo !_newline!>"%_metaDataFile%"
) else (
set _newline=%%a
echo !_newline!>>"%_metaDataFile%"
)
set /a x+=1
)
My question is two fold...is there a better way to do this? I also have not figured out how to write to the _metaDataFile without first dumping !_tmdbReturn! into a txt file. I've tried replacing the command of the last For Loop with
for /f "tokens=* delims=#" %%a in ('echo !_tmdbReturn!') do (
Only the first token writes yet
echo !_tmdbReturn!
displays the data properly producing the following:
{page:1
results:[{poster_path:\/5ttOaThDVmTpV8iragbrhdfxEep.jpg
adult:false
overview:At the height of the Cold War, a mysterious criminal organization plans to use nuclear weapons and technology to upset the fragile balance of power between the United States and Soviet Union. CIA agent Napoleon Solo and KGB agent Illya Kuryakin are forced to put aside their hostilities and work together to stop the evildoers in their tracks. The duo's only lead is the daughter of a missing German scientist, whom they must find soon to prevent a global catastrophe.
release_date:2015-08-13
genre_ids:[35,28,12]
id:203801
original_title:The Man from U.N.C.L.E.
original_language:en
title:The Man from U.N.C.L.E.
backdrop_path:\/bKxcCNv2xq8M3GD5iSrv9bMGDVa.jpg
popularity:5.346674
vote_count:1842
video:false
vote_average:7},{poster_path:\/3VScfiBmE1loQxMkuN1suALv4f8.jpg
adult:false
overview:When THRUSH steals a nuclear weapon and demands a ransom delivered by Napoleon Solo, UNCLE recalls him and his partner to duty.
release_date:1983-04-05
genre_ids:[28,80,53,10770]
id:94116
original_title:The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair
original_language:en
title:The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair
backdrop_path:\/5LGBhGg5Tj9OSW4rD0itz0sYKPT.jpg
popularity:1.046707
vote_count:5
video:false
vote_average:3.6}]
total_results:2
total_pages:1}
I'm attempting to redirect echo !_tmdbReturn! to the Find function extracting a particular value by name. I can do it in a file using findstr, but was trying it on the variable. I'm not fluent in batch so any suggestions are appreciated.
In case its useful for someone I settled on the following:
set x=
set /a x=0
set y=
set /a y=0
:: clean up the beginning of the data replace {" with " so poster_path is passed as a value
set _tmdbReturn=%_tmdbReturn:{"="%
set _tmdbReturn=%_tmdbReturn:~0,-1%
for /F "tokens=1* delims=[" %%a in ("!_tmdbReturn!") do ( set _tmdbReturn=%%b)
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
set "line=!line:"=!"
rem Show lines of desired values only
for /F "tokens=1* delims=:" %%b in ("!line!") do (
if "%%b" equ "poster_path" set /a x+=1
if "%%b" equ "total_results" (
call set _movie.%%b=%%c
) else (
echo call set _movie[!x!].%%b=%%c
call set _movie[!x!].%%b=%%c
)
)
)
This give me an array of the returned results with structured object properties that I can use as my script morphs. This may be old hat to most, but I'm having fun!
The code below separates your long string in several lines:
#echo off
setlocal EnableDelayedExpansion
:: Do a movie title search based on a Disc Volume Label
:: for /f "usebackq delims=" %%a in (`PowerShell -Command "(new-object net.webclient).DownloadString('https://api.themoviedb.org/3/search/movie?api_key=xxx&query=The+Man+from+uncle')"`) do (set _tmdbReturn=%%a)
for /F "delims=" %%a in (input.txt) do set "_tmdbReturn=%%a"
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
echo !line:"=!
)
I stored your long line in input.txt file for my testings.
About "redirect echo !tmdbReturn! to the Find function extracting a particular value by name"; if you show what exactly you want, perhaps I could show you how to get an equivalent result in a simpler way (without using find command)...
EDIT: Show just desired values
If you want not to create a file with all lines, but just show the lines of a desired value, then you may directly look for such a value in each line:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%a in (input.txt) do set "_tmdbReturn=%%a"
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
set "line=!line:"=!"
rem Show lines of desired values only
for /F "tokens=1* delims=:" %%b in ("!line!") do (
if "%%b" equ "original_title" echo %%b: %%c
)
)
You may also look for several values; just define the list of the desired values at beginning, enclosing all values by a certain delimiter character:
set "values=/title/original_title/"
... and change the if command inside the for by this one:
if "!values:/%%b/=!" neq "%values%" echo %%b: %%c

Randomly choose between two names in batch

I'm trying to get a batch script to randomly choose between two names and output one at a time, kinda something like this:
#echo off
::I would not be prompted to enter these, they would already be programmed into the script.
ChooseRandomSet=Maria
Sean
Matt
Laura
ChooseRandom
So, then on the user side, you would see:
C:\>script.bat
Matt was randomly chosen.
C:\>script.bat
Sean was randomly chosen.
C:\>
Thanks!
First create a text file with all the names on separate lines:
Maria
Sean
Matt
Laura
Then create a .bat file with the following code:
#ECHO OFF
IF "%~1"=="" (ECHO No text file specified & GOTO :EOF)
IF NOT EXIST %1 (ECHO Text file doesn't exist. & GOTO :EOF)
FOR /F "" %%I IN ('FIND /C /V "" ^<%1') DO SET /A lines=%%I
IF %lines%==0 (ECHO Text file is empty or unreadable & GOTO :EOF)
SET /A skip=(%RANDOM%*32768+%RANDOM%)%%lines
<%1 (
FOR /L %%I IN (1,1,%skip%) DO (
SET /P line=
)
SET line=
SET /P line=
)
ECHO(%line%
This will print out a name from the file, it will also check for certain errors like:
No text file specified
File specified but non-existent
Empty text file
Disclaimer:
I used this bat script for something but I didn't write it, I might've changed a few things over the years but it's largely as I found it a while back. (not a Windows guy myself.. :P)
Update: I also found an answer to this myself just by experimenting. This chooses one option out of two possible choices:
#echo off
if %random% gtr %random% (
echo heads
) else (
echo tails
pause

Cannot echo in the same line in Batch script

I have txt files that contain several lines and I need to create a log out of them to store in a log the following information:
File Name
Last modified
Count of lines containing the word "valid"
I've put together a .bat file but it splits the output in two lines.
type nul > FilesReceived.txt & for %f in (*.log) do (
find /c "valid" %f & echo(%~tf)>> LogsReceived.txt
)
With type nul I clear the contents of the FilesReceived.txt file. Then I loop through the files of type log.
Then I count lines that contain the word valid with find /c and I also echo the last modified time stamp.
However the output looks like:
---------- transaction_20160505_1005A.log: 6492
10/06/2016 04:37 p.m.
I don't know what's generating those dashes. Ultimately I'd like to have one line per log file as follows:
transaction_20012B.log: 6492 10/06/2016 04:37 p.m.
Hope you guys can help me.
Thanks,
Bruce
find prints the dashes if it processes a file. It doesn't, when processing from STDIN (type file.ext /c |find "string" prints the count only).
There is a trick to write without linefeed: <nul set /p"=Hello"
If you can live with another order, it's quite easy to assemble it::
#echo off
for %%f in (*.bat) do (
<nul set /p "=%%f %%~tf "
type %%f|find /c "echo"
)
If you want to keep your order it's a little bit more complicated: you can't force find to write without linefeed, so you have to use a trick (another for):
#echo off
(for %%f in (*.txt) do (
<nul set /p "=%%f: "
for /f %%i in ('type %%f^|find /c "valid"') do (<nul set /p "=%%i ")
echo %%~tf
))>LogsReceived.txt
You may get the output of find command via another for and put it at any place you wish:
#echo off
(for %%f in (*.log) do (
for /F %%c in ('find /c "valid" ^< %%f') do echo %%f: %%c %%~tf
)) > LogsReceived.txt

Concerning batch games, find and replace a string

I have made a character file in which my game pulls data and variables from. Looks like so:
playerName= Marche
playerRace= Elf
playerHP= 100
playerSPD= 200
playerATK= 120
playerDEF= 70
Final Fantasy reference anyone...? Anyway, when the character levels up, I need a batch script to find the string "playerHP= 100". playerHP is set as a variable within the script. Basically, it takes the current health, and multiplies it by 120%, increasing the number. How do I echo the result of that math to replace the current number?
For example if that didn't make any sense, I have 100 health. I level up, thus increasing my health stat by 120%, so now I have 120 health. I would want to find the string "playerHP= 100" and replace it with "playerHP= 120".
If it can be avoided I don't want to download any other commands (I've seen sed a few times). Thanks much
EDIT: Instead of searching for the string and replacing I took jeb's advice and just deleted the file and re-echoed all of the data. It ended up looking like this:
set /a LeveledUpPlayerHP=(%ppHP%* 12) / (10)
set /a LeveledUpPlayerSPD=(%ppSPD%* 12) / (10)
set /a LeveledUpPlayerATK=(%ppATK%* 12) / (10)
set /a LeveledUpPlayerDEF=(%ppDEF%* 12) / (10)
echo Updating stats...
del "C:\Users\%USERNAME%\Desktop\CMDRPG\player\playerData.dll
ping 1.1.1.1 -n 1 -w 500 > nul
echo playerName= %playerName%>playerData.dll
echo playerRace= %playerRace%>>playerData.dll
echo playerHP= %LeveledUpPlayerHP%>>playerData.dll
echo playerSPD= %LeveledUpPlayerSPD%>>playerData.dll
echo playerATK= %LeveledUpPlayerATK%>>playerData.dll
echo playerDEF= %LeveledUpPlayerDEF%>>playerData.dll
The playerName and playerRace are all loaded in prior to this section of the code. Ping is used just as a wait function to let the file delete before echoing new data. Seems to work okay. Thanks all
try this (output is in %inifile%.new):
#ECHO OFF &SETLOCAL ENABLEDELAYEDEXPANSION
SET "inifile=file"
FOR /f %%a IN ('^<"%inifile%" find /c /v ""') DO SET /a lines=%%a
< "%inifile%" (
FOR /l %%a IN (1,1,%lines%) DO (
SET "line="
SET /p "line="
IF NOT "!line:playerHP=!"=="!line!" (
FOR /f "tokens=2delims= " %%b IN ("!line!") DO SET /a HP=%%b*12/10
SET "line=playerHP= !HP!"
)
ECHO(!line!
))>"%inifile%.new"
Input/output:
>type file
playerName= Marche
playerRace= Elf
playerHP= 100
playerSPD= 200
playerATK= 120
playerDEF= 70
>test.bat
>type file.new
playerName= Marche
playerRace= Elf
playerHP= 120
playerSPD= 200
playerATK= 120
playerDEF= 70
Presumeably it does not matter what order the values appear in you file. If that is so, then the following will effectively "edit" your file.
It first uses FINDSTR to isolate the current playerHP value, and FOR /F to parse out the value from the string. SET /A increments the playerHP. Then a new file is created using FINDSTR to write all the current values except for playerHP, and then the new playerHP info is appended to the end.
#echo off
set "file=gameData.txt"
for /f "tokens=2 delims==" %%N in ('findstr /bl "playerHP=" "%file%"') do set /a "newHP=%%N*120/100"
>"%file%.mod" (
findstr /blv "playerHP=" "%file%"
echo playerHP=%newHP%
)
move /y "%file%.mod" "%file%" >nul
But why go to all that trouble. I should think you already have all your values in memory as environment variables. Simply make sure that all variables that are to be stored in a file start with a common unique prefix. In your example, they all start with "player", but you probably want something a little more generic - perhaps a symbol like #. So you could have #playerName, #playerRace, etc.
Simply update the values in memory as needed. When it comes time to save the current game state to a file, then all you need to do is
>"gameData.txt" set #
A brand new file will be created each time containing all the current values.
To load the values, you do exactly what jeb suggested:
for /f "usebackq delims=" %%A in ("gameData.txt") do set %%A
To save your data in a file, you could use a block like
(
echo playerName=%playerName%
echo playerRace=%playerRace%
echo playerHP=%playerHP%
) > player.ini
And to load it you could use
for /F "delims=" %%L in (player.ini) do set "%%L"

Extract a string between quotes using batch

I have "input.txt" and I want to search for a string and extract a string between quotes
my input.txt have only 2 lines :
name=' james carter' nationality = ' usa ' age=32
name='fabio rossi' nationality = ' italia - milano ' age=19
I want the batch to extract the names (with spaces) from quotes and put them in variables (in this case only two variables is needed). For example I want to set two variables:
name= james carter
name=fabio rossi
So I can use these variables in batch to do some future maniplation.
ps. its ok to use sed if necessary
edit
it seems to be impossible , so I wonder if can just take the string keeping quotes ,like this
name1=' james carter'
name2='fabio rossi'
so the problem now is,to take what start after name=' and end with '
You can try this:
#echo off
setlocal enabledelayedexpansion
set "INPUT_FILE=input.txt"
set "SED_CMD=sed "s/name='\(.[^^']*\)'.*/\1/""
set i=0
for /f "usebackq tokens=*" %%a in (`findstr "name=" %INPUT_FILE%`) do (
set /a i+=1
for /f "usebackq tokens=*" %%v in (`echo %%a ^| %SED_CMD%`) do (
echo %%v
)
)
You will then have enumerated variables (name1, name2), etc. with the values you want.
Note that this works on any line with the specified form (i.e. name='something' blah blah).
EDIT: modified the code to discard lines that don't contain the string name=.
If each line of interest is always formatted the same, starting with name=', then a native batch solution is simple:
#echo off
setlocal
set input="test.txt"
for /f "tokens=1,3 delims=:'" %%A in (
'findstr /b /c:"name='" %input% ^| findstr /n "^"'
) do set "name%%A=%%B"
This solution assumes that the name value never contains the : character.

Resources