How do I split directory addresses (recusively) in a batch file? - windows

I have folders of audio samples, and I'm about to delete what I don't use, but I'd like to have a record of the original contents in an SQLite database.
Currently, I'm writing directly to the database, but I think writing to a .CSV file and importing to a database would be faster because there are hundreds of files.
Use this batch code to create a directory tree like my file system + dummy files, for testing. It will create within wherever you execute the file. (In my case, the top folder was named Testing :
SET "BigFish=Z. Unsorted\Big Fish Audio\Midnight Jazz Train\Brass and Winds"
SET "Fragments=Z. Unsorted\Loopmasters\Fragments 02\FR2_SOUNDS_&_FX\FR2_BASS"
SET "LCycles=Z. Unsorted\Loopmasters\Hip-Hop Lunar Cycles\LUN_SYNTH_MELODY_LOOPS"
FOR %%A IN (Tenor_Sax, Trumpet) DO MD "%BigFish%\%%A"
MD "%Fragments%"
MD "%LCycles%"
#echo justtestjunk > "%Fragments%\loop.wav"
#echo justtestjunk > "%LCycles%\drum.aiff"
#echo justtestjunk > "%LCycles%\file.txt"
FOR %%A IN (Tenor_Sax, Trumpet) DO echo justtestjunk > "%BigFish%\%%A\data.sql"
With help, I've managed to create a database and make some entries, but my output looks as follows:
When what I want is this:
I think the problem is with the delimiting in the code.
Also, I need the code to handle the absence of a Type value (leaving it NULL)
So...
Current Code
#echo off
setlocal enabledelayedexpansion
SET fld="Z. Unsorted"
sqlite3 %fld%\TestDb.sqlite "CREATE TABLE IF NOT EXISTS \"Samples\" (File TEXT NOT NULL, Publisher TEXT, Pack TEXT NOT NULL, Category TEXT, Type TEXT, Size INT);"
for /r %fld% %%A in (*.aiff *.wav) do (
call :part "%%~dpA"
set "File=%%~nxA"
set "File=!File:'=''!"
sqlite3 %fld%\TestDb.sqlite "INSERT OR REPLACE INTO \"Samples\" VALUES ('!File!', '!Publisher!', '!Pack!', '!Category!', '!Type!', '%%~zA');"
#echo '!File!', '!Publisher!', '!Pack!', '!Category!', '%%~zA'
)
PAUSE
:part
for /f "tokens=6-10 delims=\" %%A in ("%~1") do (
set "Publisher=%%~A"
set "Publisher=!Publisher:'=''!"
set "Pack=%%~B"
set "Pack=!Pack:'=''!"
set "Category=%%~C"
set "Category=!Category:'=''!"
set "Type=%%~D"
set "Type=!Type:'=''!"
)

#echo off
setlocal enabledelayedexpansion
set "fld=Z. Unsorted"
> "%fld%\TestDb.sql" (
echo BEGIN;
echo CREATE TABLE IF NOT EXISTS "Samples" ^(File TEXT NOT NULL, Publisher TEXT, Pack TEXT NOT NULL, Category TEXT, Type TEXT, Size INT^);
)
for /r "%fld%" %%A in (*.aiff *.wav) do (
set "relative_path=%%~dpA"
set "relative_path=!relative_path:%cd%=!"
if "!relative_path:~,1!" == "\" set "relative_path=!relative_path:~1!"
call :part "!relative_path!"
set "File=%%~nxA"
set "File='!File:'=''!'"
>> "%fld%\TestDb.sql" echo INSERT OR REPLACE INTO "Samples" VALUES ^(!File!, !Publisher!, !Pack!, !Category!, !Type!, %%~zA^);
#echo !File!, !Publisher!, !Pack!, !Category!, !Type!, %%~zA
)
>> "%fld%\TestDb.sql" echo COMMIT;
sqlite3 -cmd ".read '%fld%\TestDb.sql'" "%fld%\TestDb.sqlite" ""
pause
exit /b
:part
for /f "tokens=2-5 delims=\" %%A in ("%~1") do (
set "Publisher=%%~A"
if defined Publisher (
set "Publisher='!Publisher:'=''!'"
) else set "Publisher=NULL"
set "Pack=%%~B"
if defined Pack (
set "Pack='!Pack:'=''!'"
) else set "Pack=''"
set "Category=%%~C"
if defined Category (
set "Category='!Category:'=''!'"
) else set "Category=NULL"
set "Type=%%~D"
if defined Type (
set "Type='!Type:'=''!'"
) else set "Type=NULL"
)
exit /b
The code uses enabledelayedexpansion for some variables. That is
for variables using ! instead of %.
Uses a for loop to parse directories using recursion to find files.
The call :part "!relative_path!" line gets the parts of the path of %fld%
that match the columns values of Publisher, Pack, Category and Type.
Then it goes through the files in that directory and inserts the
sql statements into the sql file.
The sql inserted values that may contain ' are escaped to be ''.
The for loop in the label :part uses tokens of 2-5. This will
set %%A to Publisher, %%B to Pack, %%C to Category and
%%D to Type. Adjust token setting of 2-5 to what is needed to
match the path segments.
Current variables set in label :part are set as NULL if the variable
is not defined else the variable is single quote escaped and outer
single quotes are added. Thus, ready for insert as is. Similar done
to the File variable in the for loop.
The relative_path variable is the absolute path with %cd% removed
from it and possible leading backslash removed. It should be easier to
tokenize in the for loop in the label of :part.

Related

This automated file to dirtree match/sort calculator is getting too complex

I have this semi-automated file sorting script that, for every subdirectory in %destination% check for matches against %mask% and count those matches, then write NumberOfMatches,DirectoryPath to matches.csv for later sorting to find the most matches
:count
setlocal enabledelayedexpansion
:: for each dir in destination count matches for mask
for /f "delims=" %%a in ('dir /b /s /ad "%destination%\*" 2^>nul') do (
set "count="
for /f %%b in ('dir /b "%%a\%mask%" 2^>nul ^|find /c /v ""') do (
set "count= %%b"
echo !count:~-4!,%%a >>"%tmp%\matches.csv"
)
)
:: by sorting the results we have a winner
for /f "tokens=1,* delims=, " %%a in ('sort "%tmp%\matches.csv"') do set "fulldir=%%b" & set "count=%%a"
This is not entirely ideal since I need to come up with the search masks and keep a list. I think I could find proper matches completely automatically instead by dividing the filename to sets of two or more consecutive words and use them for the search like so (criticism about idea required)
set destionation=d:\texts
:: example file
set "orphan=this file name right here.txt" & set "oname=this file name right here"
for /f "tokens=1,2,3,4,5,6,7,8,9,*" %%i in ("%oname%") do (
if not "%%i"=="" if not "%%j"=="" set "mask=*%%i %%j*" & call :count
if not "%%j"=="" if not "%%k"=="" set "mask=*%%j %%k*" & call :count
if not "%%k"=="" if not "%%l"=="" set "mask=*%%k %%l*" & call :count
if not "%%l"=="" if not "%%m"=="" set "mask=*%%l %%m*" & call :count
if not "%%m"=="" if not "%%n"=="" set "mask=*%%m %%n*" & call :count
if not "%%n"=="" if not "%%o"=="" set "mask=*%%n %%o*" & call :count
if not "%%o"=="" if not "%%p"=="" set "mask=*%%o %%p*" & call :count
if not "%%p"=="" if not "%%q"=="" set "mask=*%%p %%q*" & call :count
if not "%%q"=="" if not "%%r"=="" set "mask=*%%q %%r*" & call :count
)
exit /b
This has the undesired result of each different %mask% count being recorded on its own and not added up with the others, which would give a better directory match.
For example, my example input this file name right here.txt may produce results like this using the first %mask% (pair of words) *this file*
3,d:\texts\word
2,d:\texts\notes
And next on the list there would be separate results from the next %mask% word pair *file name*
2,d:\texts\notes
I need these results for these different pairs to be added up so the results for these two first pairs would instead look like this
3,d:\texts\word
4,d:\texts\notes

Not Another Batch File Nested For Loop Question?

I'm trying to find a way to make nested for loops work, but this iteration is different than the most popular results (where an OP is looping through directories, or using a numerical for /l loop, etc.)
Instead, I'm trying to figure out how to make this work:
#echo Off
setlocal enabledelayedexpansion enableextensions
for /f "Tokens=1-7 Delims=_" %%P in ("Testing_This_GREAT_Thing_Of_An_IDEA") do (
Echo %%P
For %%a in (P Q R S T U V ) do (
Call set "term=%%%%%%a"
Call echo !term!
Call set term=%%term%%
Call Echo !term!
Call set term=%%term%%
Call Echo !term!
If not "!term!"=="" Call set word.%%a=%%term%%
Echo word.%%a = "!word.%%a!"
)
)
pause
exit /b
Desired output of For %%a in (P Q R S T U V) loop would be to have:
word.P=Testing
word.Q=This
word.R=GREAT
word.S=Thing
word.T=Of
word.U=An
word.V=IDEA
Obviously the following would be as expected for the initial loop, but I cannot get the delayed expansion (I assume) to work as expected. . . .
%%P=Testing
%%Q=This
%%R=GREAT
%%S=Thing
%%T=Of
%%U=An
%%V=IDEA
No, it's not possible (in a single block).
You try to dynamically access a FOR meta variable, but FOR meta variables are recognized only in the parsing phase, before the code block is executed.
An inline call can't help here, because a FOR meta variable isn't detected at all in the second parsing round of a call command.
But you could use a helper function, using the fact that you can access all FOR meta variables in any FOR block, even when they are not in the same block.
#echo Off
setlocal enabledelayedexpansion enableextensions
for /f "Tokens=1-7 Delims=_" %%P in ("Testing_This_GREAT_Thing_Of_An_IDEA") do (
Echo %%P
For %%a in (P Q R S T U V ) do (
Call :read_meta_var term %%a
If not "!term!"=="" Call set word.%%a=%%term%%
Echo word.%%a = "!word.%%a!"
)
)
pause
exit /b
:read_meta_var
REM *** %1=Variable to store the result
REM *** %2=Character of the meta variable to read
for %%d in ("dummy") do (
set "%1=%%%2"
)
Adding another answer I stumbled upon which operates on the premise presented by #T3RR0R's Comment to my original post - #T3RR0R Linked to this article at dostips.com, where we are presented with the idea of parsing substrings from one string using whatever delimiter we want.
Modifying one of the examples set within this article, we can arrive at the following which uses a zero-indexed "array" like we're used to seeing in almost all other programming languages.
set "x=Testing_This_GREAT_Thing_Of_An_IDEA"
set i=0
set "x.!i!=%x:_=" & set /a i+=1 & set "x.!i!=%"
REM output x var's:
set x.
Which yields the fantastically succinct output of:
x.0=Testing
x.1=This
x.2=GREAT
x.3=Thing
x.4=Of
x.5=An
x.6=IDEA
I played around with trying to set "x.a"-"x.g" using variable positioning and the delayed expansion of i where it is asserted that "verb=abcdefghijklmnopqrstuvwxyz", by using:
set "x.!y!=%x:_=" & set /a i+=1 & Call set "y=%%verb:~!i!,1%%" & set "x.!y!=%"
Unfortunately, it always ended with x.a=Testing and nothing else defined, while y=IDEAverb:~7,1 always occurred, boggling my mind. . . I would love to get this to work in a one-liner!
Fortunately, if I take a For /l numerical loop, I can accomplish my goal in 2 lines with the assertion that x and verb are already set previously as described using the original substitution line:
set "x.!i!=%x:_=" & set /a i+=1 & set "x.!i!=%"
For /l %%l in (0,1,!i!) do (For /f "Tokens=1" %%q in ("!verb:~%%l,1!") do ( set "word.%%q=!x.%%l!" ) )
If it is possible to perform this variable substitution/positioning in one line using the calculation to i in one line, this would be fantastic, otherwise, for the sake of sanity, an extra line is not a problem for me.
I think you should read the whole article at dostips.com because in the last page this method is explained:
#echo off
setlocal EnableDelayedExpansion
set "str=Testing_This_GREAT_Thing_Of_An_IDEA"
set "vars=P Q R S T U V"
set "p=%%" & set "v=%vars: =" & set "s=!str:*_=!" & call set "word.!v!=!p!str:_!s!=!p!" & set "str=!s!" & set "v=%" & set "word.!v!=!s!"
set word
Output:
word.P=Testing
word.Q=This
word.R=GREAT
word.S=Thing
word.T=Of
word.U=An
word.V=IDEA
EDIT: New method added
After carefully read your answer, I think this is what you are looking for:
#echo off
setlocal EnableDelayedExpansion
set "x=Testing_This_GREAT_Thing_Of_An_IDEA"
set "verb=abcdefghijklmnopqrstuvwxyz"
set "p=%%" & set i=0 & set "x.%verb:~0,1%=%x:_=" & set /a i+=1 & call set "x.!p!verb:~!i!,1!p!=%"
set x
Finally, a more "traditional" method, using just FOR's:
#echo off
setlocal EnableDelayedExpansion
set "x=Testing_This_GREAT_Thing_Of_An_IDEA"
set "y=P Q R S T U V"
for %%n in (^"^
%Don't remove this line%
^") do for %%a in ("!x:_=%%n!") do (
for /F "tokens=1*" %%b in ("!y!") do set "word.%%b=%%~a" & set "y=%%c"
)
set word

Get position of substring by match index in batch script

I want to find the position of a substring by match index. To be precise, say there are 6 matches in a string and I want the position of match number 4. Example:
set "myString=barbarbarfoobarfoobarbar"
set "myMatch=bar"
set myMatchIndex=4
... do stuff ...
echo Position is: %position%
Position is: 13
The following will produce the desired result.
Does not work with special characters such as (!) and ("), make sure to properly sanitize inputs.
:indexOf
setlocal ENABLEDELAYEDEXPANSION
set indexOf_StringPosition=0
set "indexOf_Var1=%~1"
set "indexOf_Var2=%~2"
set indexOf_Var3=%3
:indexOf_NoInitLabel
set indexOf_CurrentPosition=0
set "indexOf_StringTemp=!indexOf_Var1:*%indexOf_Var2%=!"
if "%indexOf_StringTemp%"=="%indexOf_Var1%" (
set indexOf_StringPosition=-1
goto :indexOf_BreakLabel
)
set "indexOf_StringTemp2=!indexOf_Var1:%indexOf_Var2%%indexOf_StringTemp%=!"
if "%indexOf_StringTemp2%"=="" (
set /a indexOf_CurrentPosition+=1
goto :indexOf_BreakLabel
)
:indexOf_LoopLabel
if "!indexOf_StringTemp2:~%indexOf_CurrentPosition%,1!"=="" (
set /a indexOf_CurrentPosition+=1
goto :indexOf_BreakLabel
)
set /a indexOf_CurrentPosition+=1
goto :indexOf_LoopLabel
:indexOf_BreakLabel
set /a indexOf_StringPosition+=%indexOf_CurrentPosition%
set /a indexOf_Var3-=1
set "indexOf_Var1=!indexOf_Var1:~%indexOf_CurrentPosition%!"
if "indexOf_Var1"=="" (
goto :indexOf_EndLabel
)
if "%indexOf_Var3%"=="0" (
goto :indexOf_EndLabel
)
goto :indexOf_NoInitLabel
:indexOf_EndLabel
(endlocal & set %4=%indexOf_StringPosition%)
goto :eof
indexOf can be called like so:
#echo off
set "myString=barbarbarfoobarfoobarbar"
set "myMatch=bar"
set myMatchIndex=4
call :indexOf "%myString%" "%myMatch%" %myMatchIndex% position
echo Position is: %position%
pause
rem Position is: 13
​
Important Note: The algorithm's index starts at 1. This means "b" is treated as 1, "a" is treated as 2, and so on. To start the index at 0, add the following after :indexOf_EndLabel:
if not %indexOf_StringPosition%==-1 set /a indexOf_StringPosition-=1

Windows Batch File : Convert Structures into Single Lines Strings

This hard task I need to do as a batch file, it's not the most difficult to do with C but in DOS is a hell (at least for me!) I need to convert a structure to a single var (string) to be able to convert them into that structure again inside my program. Don't worry about the comeback, it's already done.
The struct WILL vary in size and have an index inside [0] which need to be the first item in my var.
Here an exemple of these structures (just 3):
level.waypoints[0] = spawnstruct();
level.waypoints[0].origin = (1774.5,834.7,67.6);
level.waypoints[0].type = "stand";
level.waypoints[0].childCount = 2;
level.waypoints[0].children[0] = 1;
level.waypoints[0].children[1] = 6;
level.waypoints[1] = spawnstruct();
level.waypoints[1].origin = (1780.6,639.1,65.5);
level.waypoints[1].type = "stand";
level.waypoints[1].childCount = 2;
level.waypoints[1].children[0] = 7;
level.waypoints[1].children[1] = 0;
level.waypoints[2] = spawnstruct();
level.waypoints[2].origin = (1902.2,-345.2,74.2);
level.waypoints[2].type = "stand";
level.waypoints[2].childCount = 2;
level.waypoints[2].children[0] = 3;
level.waypoints[2].children[1] = 97;
level.waypoints[3] = spawnstruct();
level.waypoints[3].origin = ... (goes on...)
So the first structure:
level.waypoints[0] = spawnstruct();
level.waypoints[0].origin = (1774.5,834.7,67.6);
level.waypoints[0].type = "stand";
level.waypoints[0].childCount = 2;
level.waypoints[0].children[0] = 1;
level.waypoints[0].children[1] = 6;
I need to convert this structure into this line:
set flwp_0 "0,1774.5,834.7,67.6,stand,2,1,6"
Where 0 (flwp_) it's the index and also the first item inside "".
So the other 2 in this exemple will be:
set flwp_1 "1,1780.6,639.1,65.5,3,7,0,30"
set flwp_2 "2,1902.2,-345.2,74.2,2,3,97"
The childCount could be any value, but I don't believe will pass than 9.
So, how many childCount we have, more children will be in the next lines.
But I just need the values after the childCount value, so I with that value I know how much more items will be available in that string, so no problem.
I'm thinking removing everything I don't need first like (in order):
level.waypoints[
] = spawnstruct();
].origin = (
);
].type = "
";
].childCount =
].children[0] =
].children[1] =
].children[2] =
].children[3] =
].children[4] =
].children[5] =
].children[6] =
].children[7] =
].children[8] =
].children[9] =
;
This will left something like this in the first structure:
0
01774.5,834.7,67.6
0stand
02
01
06
Now I need to read that and transform into my single line string. But I'm having problems to know the right index because could be the first value, but if the index have 2 or 3 chars?
I also was thinking in replace with some info to track me, so will not replace the "]" of the files, so I'll know what is before that is the index and after that the values.
I was also trying to a loop without success creating a index like:
level.waypoints[X]
Where X is from 0 to 1000 (I don't believe will pass that number)
It's not working, maybe my first idea was better... :\
I need a batch file (or several) to convert several files which have structures like that inside to new files with the values as strings as I wrote in details above.
Any ideas?
Thank you!
#Squashman Also the result is a little messed up in the order as you can see:
set flwp_0 "0,1774.5,834.7,67.6,stand,2,1,6"
set flwp_100 "100,1636.76,371.924,240.125,stand,2,101,99"
set flwp_101 "101,1861.45,437.846,240.125,stand,2,102,100"
set flwp_102 "102,1843.93,557.03,240.125,stand,2,103,101"
set flwp_103 "103,1504.58,553.357,64.125,stand,2,8,102"
set flwp_104 "104,653.17,1675.32,64.125,stand,2,26,105"
set flwp_105 "105,338.784,1680.49,232.125,stand,2,104,133"
set flwp_106 "106,-919.398,1537.7,80.125,stand,3,107,109,150"
set flwp_107 "107,-928.311,1111.47,80.125,stand,3,108,106,149"
set flwp_108 "108,-696.488,1095.93,80.125,stand,2,36,107"
set flwp_109 "109,-787.781,1566.87,80.125,stand,2,106,110"
set flwp_10 "10,1423.3,-403.8,64.3,stand,4,11,12,15,3"
set flwp_110 "110,-754.274,1716.71,80.125,stand,2,109,34"
set flwp_111 "111,-736.201,1887.87,64.125,stand,4,112,34,151,152"
set flwp_112 "112,-454.293,1879.4,64.125,stand,4,111,33,34,152"
set flwp_113 "113,125.395,-451.579,58.6958,stand,3,51,114,142"
set flwp_114 "114,282.593,-447.87,68.125,stand,2,115,113"
set flwp_115 "115,304.311,-271.206,68.125,stand,2,116,114"
set flwp_116 "116,447.128,-292.167,68.125,stand,3,118,115,117"
set flwp_117 "117,437.415,-443.822,68.125,stand,2,67,116"
set flwp_118 "118,582.932,-297.811,126.125,stand,2,119,116"
set flwp_119 "119,561.586,-485.701,204.125,stand,2,120,118"
set flwp_11 "11,1240.1,-249.7,74.1,stand,5,9,16,10,12,15"
set flwp_120 "120,423.422,-470.754,204.125,stand,2,119,135"
set flwp_121 "121,1164.82,-1203.64,72.125,stand,4,123,14,126,122"
set flwp_122 "122,1064.12,-1299.76,72.125,stand,3,123,125,121"
set flwp_123 "123,1117.59,-1476.49,72.125,stand,3,124,122,121"
set flwp_124 "124,861.878,-1497.7,72.125,stand,2,125,123"
set flwp_125 "125,878.58,-1294.38,72.125,stand,2,124,122"
Is possible to write in the file in the right order?
thank you very much again for the help #Squashman
cheers
Ok, really doesn't matter the order, but I'm getting trouble to make do it to everyfile into a new one... I tryed this without success:
#echo off
setlocal enabledelayedexpansion
for %%f in (*.gsc) do (
FOR /F "tokens=1-2 delims==;^(^) " %%G IN (%%f) do set %%~G=%%~H
FOR /F "tokens=2 delims=[]" %%G IN ('set level.waypoints ^|find /I "spawnstruct"') do (
SET waypoints=!waypoints! %%G
)
FOR %%G IN (%waypoints%) do (
set line=%%G,!level.waypoints[%%G].origin!,!level.waypoints[%%G].type!,!level.waypoints[%%G].childCount!
FOR /F "tokens=2 delims==" %%H IN ('set level.waypoints[%%G].children') DO set line=!line!,%%H
set line=set flwp_%%G "!line!"
echo !line! >> %%f.cfg
)
)
pause
Any clues?
=======================================
Ok, now we are VERY close! Need to process ONLY the lines with level.waypoints[ and the rest must be ignored, and also add a final close var that I already did with success below:
#echo off
setlocal enableDelayedExpansion
set findtext="level.waypoints["
for %%F in (*.gsc) do (
set "out="
set "i=x"
> "%%~nF.cfg" (
for /f usebackq^ tokens^=2^,4^,5^ delims^=[]^=(^)^;^"^ %%A in ("%%F") do (
if %%A neq !i! (
if defined out echo !out!"
set /a "i=%%A, j=0"
set "out=set flwp_!i! "!i!"
) else (
set /a j+=1
if !j! leq 3 (set "out=!out!,%%B") else set "out=!out!,%%C"
)
)
if defined out echo !out!"
set /a "fim=i+1"
echo set flwp_!fim! "eof"
)
)
Now how can I add findstr %findtext% into these loops? I tryed in many different ways, but the syntax is wrong... Could you help me? :D
Thank you very much #Squashman and #dbenham for all the help until now.
Cheers
Here is a relatively fast pure batch solution that uses a single FOR /F loop for each file. It is significantly faster than Squashman's solution.
I set DELIMS and TOKENS to parse out all needed data elements, no matter which line I am parsing. I don't worry about the value of childCount. Instead I simply detect the change of the waypoint index to signal the start of a new record, and an incrementing waypoint line number (j variable) is used to identify which tokens are needed from each line. Each line adds new text to the out variable.
#echo off
setlocal enableDelayedExpansion
for %%F in (*.gsc) do (
set "out="
set "i=x"
> "%%~nF.cfg" (
for /f usebackq^ tokens^=2^,4^,5^ delims^=[]^=(^)^;^"^ %%A in ("%%F") do (
if %%A neq !i! (
if defined out echo !out!"
set /a "i=%%A, j=0"
set "out=set flwp_!i! "!i!"
) else (
set /a j+=1
if !j! leq 3 (set "out=!out!,%%B") else set "out=!out!,%%C"
)
)
if defined out echo !out!"
)
)
The above is able to process a 23 MB file on my machine in 70 seconds.
But I can do better if you allow yourself to go outside the bounds of pure batch. Here is an efficient JREPL.BAT solution that makes use of new version 6.0 features that were released just moments ago.
JREPL.BAT is a regular expression text processing utility for Windows. It is pure script (hybrid batch/Jscript) that runs natively on any Windows machine from XP onward. No 3rd party exe files are required.
Extensive documentation is available from the command line via jrepl /?, or jrepl /?? for paged help.
This solution should be able to handle files approaching 1 GB in size. Each file is processed completely by a single call to JREPL.BAT. It should be very efficient, as there is minimal back-tracking required. For small files, it will be slower than the pure batch method due to the startup time of the CSCRIPT engine. But for large files it is much faster than pure batch.
The code below processes the same 23 MB file in only 11 seconds :-)
This first version breaks construction of the complicated regex terms into steps, and includes commented numbers above each captured group to give you a chance at fathoming how it works. In particular, read the documentation for the /T and /P options. And also bone up on your regex skills!
#echo off
setlocal
set "filter=(.*?\[\d+])[\s\S]*?(?:\n(?!\1)|(?![\s\S]))"
:: 1 2 3 4
set "find1=l.*?\[(\d+)\][\s\S]*?\((\d.*?)\)[\s\S]*?\q(.*?)\q"
set "repl1=set flwp_$2 \q$2,$3,$4"
:: 5 6
set "find2=;[\s\S]*? = (\d+)"
set "repl2=,$6"
:: 7
set "find3=[\s\S]+"
set "repl3=\q\n"
set "find=%find1%|%find2%|%find3%"
set "repl=%repl1%|%repl2%|%repl3%"
for %%F in (*.gsc) do (
call jrepl.bat "%find%" "%repl%" /t "|" /p "%filter%" /x /m /f "%%F" /o "%%~nF.cfg"
)
And here is the exact same solution, but without showing any of the intermediate steps. I did include line continuation to improve readability.
#echo off
for %%F in (*.gsc) do (
call jrepl.bat "l.*?\[(\d+)\][\s\S]*?\((\d.*?)\)[\s\S]*?\q(.*?)\q|;[\s\S]*? = (\d+)|[\s\S]+"^
"set flwp_$2 \q$2,$3,$4|,$6|\q\n"^
/p "(.*?\[\d+])[\s\S]*?(?:\n(?!\1)|(?![\s\S]))"^
/t "|" /x /m /f "%%F" /o "%%~nF.cfg"
)
I suppose the fastest script solution would be a custom built JScript or VBS script. But I enjoy working with JREPL :-)
Update: Here is a little bonus - a JREPL script that will convert *.cfg back to *.gsc
#echo off
setlocal
set "beg=var pre,i,q='\x22',n='\r\n'"
:: 1 2 3 4 5 67
set "find=^.*?\q(\d+),(.*?,.*?,.*?),(.*?),(.*?),|(.*?)[,\q]"
set "repl=i=0;pre='level.waypoints['+$2+']';$txt=pre+' = spawnstruct();'"
set "repl=%repl%+n+pre+'.origin = ('+$3+');'"
set "repl=%repl%+n+pre+'.type = '+q+$4+q+';'"
set "repl=%repl%+n+pre+'.childCount = '+$5+';'"
set "repl=%repl%|$txt=n+pre+'.children['+(i++)+'] = '+$7+';'"
for %%F in (*.cfg) do (
call jrepl.bat "%find%" "%repl%" /x /t "|" /jq /jbeg "%beg%" /f "%%F" /o "%%~nF.gsc"
)
Edited the code with your new requests.
#echo off
FOR %%F IN (*.gsc) DO (
setlocal enabledelayedexpansion
FOR /F "usebackq tokens=1-2 delims==;^(^) " %%G IN (`find /I "level.waypoints["^<"%%F"`) do set %%~G=%%~H
(FOR /F "tokens=2 delims=[]" %%G IN ('find /I "spawnstruct" ^<"%%F"') do (
set line=%%G,!level.waypoints[%%G].origin!,!level.waypoints[%%G].type!,!level.waypoints[%%G].childCount!
FOR /F "tokens=2 delims==" %%H IN ('set level.waypoints[%%G].children') DO set line=!line!,%%H
set line=set flwp_%%G "!line!"
echo !line!
))>"%%~nF.cfg"
endlocal
)
pause

Windows Batch File Quote Problems

I've got the following code, but when it is executed there are problems with the quotes: the substring methods need to use :=", but I don't know how to combine them with for example :~0,9.
SETLOCAL enabledelayedexpansion
set var=%1
if %var:~0,9%=="phpfile:/" (
set url = %var:~9%
) else (
set url = %var:~10%
)
if "%var:~-1%"=="\" (
set url = %url:~0,-1%"
)
if "%var:~-1%"=="/" (
set url = %url:~0,-1%"
)
START "" "C:\Program Files (x86)\NuSphere\PhpED\7.0\phped.exe" %url:"=%
There are some syntax bugs in your code.
Don't add spaces in SET statements, else you got variable names with spaces url<space> instead of url.
The if %var:~0,9%=="phpfile:/" ( must be quoted, else the parser will not work as expected.
The trailing quote at set url = %url:~0,-1%" will add a quote at the end, I suppose you need something like set "url=%url:~0,-1%"
set "var=%~1"
if "%var:~0,9%"=="phpfile:/" (
set "url=%var:~9%"
) else (
set "url=%var:~10%"
)
if "%var:~-1%"=="\" (
set "url=%url:~0,-1%"
)
if "%var:~-1%"=="/" (
set "url=%url:~0,-1%"
)

Resources