0

I need to loop through every line in a text file, and still have the empty lines. The common solution I could find is as follows:

for /f "tokens=1* delims=:" %%i in ('findstr /n "^" "%textfile%"') do ( call :IsLabel _return "%%b" )

This will prefix every line with the line number and colon character, example:

21:R 30 35 89jtj3 G)(#G_ 23ty9ug9dg

The problem is that, if the first character is a colon, (:), it will get dropped due to delims=: in the for loop.

So 30::mylabel becomes just mylabel instead of :mylabel.

Is there a way to make findstr use another character than colon perhaps?

-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-EDIT-

Since, no matter what, there will be extra characters to remove, I think that the best course of action is to dump the entire file into an array. Then remove the characters from that array.

So what is needed is a "FileToArray" function that takes a filename and array name and populates the array with the content of that file.

My first attempt, based on @Mofi's answer is as follows

::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
    set _FTA_buffer=%%I
    set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
    set /a "_FTA_ubound+=1"
    )
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF

(execution time, 4 second for 1600 lines on slow computer)

This version almost works, however, as @Mofi points out, is that doing this you lose all the ! characters on the line

set _FTA_buffer=%%I

@Mofi then suggests to perform the removal of the line numbers in a setlocal inside the for loop instead. In the example, this works because the data is echo'd out directly and doesn't need to be kept in a variable outside of setlocal

So, I tried this variant with a setlocal / endlocal inside the for loop

::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
    set _FTA_buffer=%%I
    setlocal enabledelayedexpansion
    set _FTA_buffer=!_FTA_buffer:*:=!
    echo set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer!
    set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer!
    set /a "_FTA_ubound+=1"
    set _FTA_ubound=!_FTA_ubound!
    endlocal & set /a "_FTA_ubound=%_FTA_ubound%" & set %_FTA_Output%[%_FTA_ubound%]=%_FTA_buffer%
    )
GoTo :EOF

(same execution time)

Unfortunately, this does not set the outputarray values.

If I echo(%_FTA_buffer% , I can see the values get passed substituted properly but can't get them out of that endlocal

I tried other permutations

::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
    setlocal disabledelayedexpansion
    set _FTA_buffer=%%I
    setlocal enabledelayedexpansion
    set /a "_FTA_ubound+=1"
    endlocal & endlocal & set /a "_FTA_ubound=!_FTA_ubound!" & set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
    )
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF

(this does not set any values)

::Usage Call :SimpleFileToArray OutputArray Filename
:SimpleFileToArray
set "_FTA_Output=%~1"
set "_FTA_ubound=1"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /n "^" "%~2"') do (
    setlocal disabledelayedexpansion
    set _FTA_buffer=%%I
    endlocal & set %_FTA_Output%[!_FTA_ubound!]=!_FTA_buffer:*:=!
    set /a "_FTA_ubound+=1"
    )
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do endlocal & set %%a
if defined _FTA_localscope endlocal
GoTo :EOF

This sets every array item to *:=

example

LinesArray[999]=*:=

To launch this function, I use this test function

:SimpleFileToArray-DEMO
Call :ClearVariablesByPrefix _FTA LinesArray
echo start SimpleFileToArray %time%
Call :SimpleFileToArray LinesArray batchsample.bat
echo end SimpleFileToArray %time%
GoTo :EOF
Shodan
  • 1,065
  • 2
  • 13
  • 35
  • Use ```%SystemRoot%\System32\find.exe /V /N "" 0^<"%textfile%"```. Then you can use the square bracket as the delimiter instead. – Compo Aug 11 '23 at 11:35
  • BTW, in order to ensure that any offered solution is robust, I woul ask that you [Edit] your code to include the `:IsLabel` section. We really need to know what is happening with the passed argument. – Compo Aug 11 '23 at 12:20
  • I suggest reading my answer on [How to read and print contents of text file line by line?](https://stackoverflow.com/a/51579256/3074564) There is your problem described in full details and which code to use to get any entire line independent on its content assigned first to a loop variable and next to an environment variable with name `Line` on which is removed the line number and just the first colon added by __FINDSTR__ on output. `Line` is either undefined after the removal of line number and colon (empty line) or is still defined with the line read from file. – Mofi Aug 11 '23 at 16:26
  • Thank you for this in-depth response, I did not know of the use of wildcard in batch file string substitution. Even knowing it, I still can't find it's syntax in the batch file tutorials even ! I've created a function to handle it :TrimBeforeChar call set %~1=%%%~3:*%~2=%% – Shodan Aug 12 '23 at 02:57
  • However, since my goal is to find the line number of every label, the name of those label and the line number of every empty line in a large batch file with many forbidden characters. I see that I will have a new approach, as I cannot endlocal and preserve the string intact when there are poison characters in it. – Shodan Aug 12 '23 at 02:58
  • So, I will post an answer to the question based on your comment – Shodan Aug 12 '23 at 02:59
  • The more I think about it, I think my question is answered by https://stackoverflow.com/questions/51576615/how-to-read-and-print-contents-of-text-file-line-by-line/51579256#51579256 but that question's title doesn't distinguish it from other answers in the important way that mine does. To keep the empty lines and not mangle characters. But @Mohi answer is the answer to my question I am almost certain now. – Shodan Aug 14 '23 at 06:59
  • I really tried to make it work, but I can't get the data out past endlocal. The answer works with echo( but not if you want to use the data elsewhere – Shodan Aug 14 '23 at 09:15

2 Answers2

1

Mmm... I read several times your question (and your answer) and I still don't understand what really is your goal... So I'm going to risk posting an answer that can be entirely useless...

This is my code:

@echo off

findstr /N "^:[^:]" test.txt | findstr /N "^"

echo/
echo Empty lines:
echo/

findstr /N "^$" test.txt

The data I used as input is your code above, in your answer.

And this is the output:

1:1::FindAllLabels-DEMO
2:14::FindAllLabelsFromFileLineArray
3:24::FindAllLabelsFromFileLineArray-loop
4:39::FindAllLabelsFromFileLineArray-skip
5:46::TrimBeforeChar
6:51::IsLabel
7:58::IsLabel-loop
8:68::IsLabel-end
9:75::GetLabel
10:83::GetLabel-loop
11:92::GetLabel-end
12:99::FileToArray
13:108::FileToArray-arguments
14:129::ClearVariablesByPrefix

Empty lines:

2:
4:
6:
8:
10:
12:
44:
49:
73:
97:
127:

In the first group the first number is a sequential number; the second one is the original line number, and the third field are the :labels in your Batch file...

So?

EDIT 2023/08/20: New code added as per comments

This code lists all functions and allows to extract any function. Check the comments in the code.

@echo off
setlocal EnableDelayedExpansion

rem Get empty lines (plus/minus one)
for /F "delims=:" %%a in ('findstr /N "^$" test.txt') do (
   set /A "p=%%a+1, n=%%a-1"
   set /A "prev[!p!]=1, next[!n!]=1"
)

cls
echo Defined functions:
rem A "function" starts in a :label preceded by an empty line
for /F "tokens=1* delims=:" %%a in ('findstr /N "^:[^:]" test.txt') do (
   if defined prev[%%a] echo %%a-  :%%b
)

:nextFunction
echo/
echo/
set /P "start=Enter line number of function to list: "
if errorlevel 1 goto :EOF
if not defined prev[%start%] echo ERROR & goto nextFunction
echo/

rem A "function" ends in a "goto :EOF" or "exit /B" line followed by an empty line
for /F "delims=:" %%a in ('findstr /N /I /C:"goto :EOF" /C:"exit /B" test.txt') do (
   if %%a gtr %start% if defined next[%%a] (
      set "last=%%a"
      goto break
   )
)
:break

setlocal DisableDelayedExpansion
set /A start-=1
for /F "skip=%start% delims=" %%a in ('findstr /N "^" test.txt') do (
   set "line=%%a"
   setlocal EnableDelayedExpansion
   echo(!line:*:=!
   endlocal
   for /F "delims=:" %%n in ("%%a") do (
      if %%n == %last% goto :break
   )
)
:break

goto nextFunction

NOTES:

  • I inserted the "documentation" on function usage in the same line of the entry point.
  • You should investigate the use of square brackets to enclose [optional elements].

This is the output using your large batch file as input (and with previous notes):

Defined functions:
232-  :skipsection
262-  :CommandToArray  OutputArray CommandString
285-  :HexToDecimal-DEMO
297-  :HexToDecimal  OutputVariable Input
305-  :DecimalToHex  OutputVariable Input
313-  :rtrim  OutputVariable Input
329-  :lenByVal  OutputResult %VariableName%
363-  :IsNumeric  Input optional Output
384-  :ClearVariablesByPrefix  myPrefix
389-  :LoopThroughArray  ArrayName optional lbound=x optional ubound=x "command 1" 
426-  :SampleLoopStructure
493-  :GetPowerSchemeContents
504-  :GetPowerSettings
685-  :ListPowerSubgroups  ArrayName optional lbound=x optional ubound=x "command 1" 
702-  :ListPowerSettings  optional NOPREFIX PowerScheme(index, name or guid) PowerSubgroup(index, name or guid) optional OutputArray
720-  :ListPowerSettingsInAllSubgroups  PowerScheme(index, name or guid) optional OutputArray
732-  :ListPowerSettingsInAllSchemes  optional OutputArray
746-  :ListAllPowerSubgroups  optional OutputArray
762-  :GetPowerSchemeIndex  Powerscheme(index, name or guid) optional OutputIndex
784-  :GetPowerSubgroupIndex  Powerscheme(index, name or guid) PowerSubgroup(index, name or guid)  optional OutputIndex
806-  :GetPowerSettingIndex  Powerscheme(index, name or guid) PowerSubgroup(index, name or guid) PowerSetting(index, name or guid) optional OutputIndex
826-  :GetPowerSchemeGuid  OutputGuid InputPowerSchemeName
831-  :GetPowerSchemeName  OutputName InputPowerSchemeGuid
836-  :GetPowerSubgroupGuid  OutputGuid InputPowerSubgroupName
841-  :GetPowerSubgroupName  OutputName InputPowerSubgroupGuid
846-  :GetPowerSettingIndex  OutputGuid InputPowerSettingName
851-  :GetPowerSettingIndex  OutputName InputPowerSettingGuid
857-  :GetDefaultSchemeName  OutputIndex OutputGuid OutputName 
867-  :GetPowerID  [schemename.[subgroupname.[settingname]
888-  :SetSettingACValue powercfg /CHANGE
895-  :ShowAllPowercfgSettings
946-  :DisableHibernation
954-  :ShowWhatIsPreventingSleep
963-  :EnableWakeByDevice
967-  :GetDevicePowerProperties
969-  :GetWakeProgrammableDevices
985-  :GetSleepStates
1050-  :GetSupportedSleepStates  optional OutputArray
1061-  :GetUnsupportedSleepStates  optional OutputArray
1072-  :GetUnsupportedSleepStatesWithReason  optional OutputArray
1083-  :SetMonitorTimeout  optional AC-timeout-minutes optional DC-timeout-minutes
1088-  :SetDiskTimeout  optional AC-timeout-minutes optional DC-timeout-minutes
1103-  :GetPowerSchemeElements  ElementString PowerSetting PowerSubgroup PowerScheme
1107-  :GetPowerSchemeValues  Powerscheme(index, name or guid) OutputArray
1125-  :ReadInputArgument  OutputVariable Input
1131-  :MakeStringLenght-DEMO
1187-  :MakeStringLenght  optional RIGHTALIGNED OutputString MaxLenght Input
1207-  :GetConsoleDimensions-DEMO
1260-  :SetConsoleDimensions-DEMO
1328-  :GetConsoleWidth  optional OutputVariable
1338-  :GetConsoleHeight  optional OutputVariable
1348-  :GetConsoleBufferWidth  optional OutputVariable
1358-  :GetConsoleBufferHeight  optional OutputVariable
1365-  :GetConsoleDimensions  WidthOutput HeightOutput
1377-  :GetConsoleBufferDimensions  WidthOutput HeightOutput
1390-  :GetCodePage  optional OutputVariable
1400-  :GetKeyboardDelay  optional OutputVariable
1410-  :GetKeyboardRate  optional OutputVariable
1417-  :GetConsoleProperties
1441-  :SetConsoleDimensions  width height
1445-  :StringRepeat-DEMO
1489-  :StringRepeat  OutputVariable Count Input
1507-  :sqrt  Input optional OutputVariable
1520-  :SquareRoot-DEMO
1542-  :GetCirclePoint  Xvalue Radius optional Output
1552-  :GetCirclePoint-DEMO
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Hello, thank you for giving a crack at this. You are answering the underlying question to the title question. Which is basically, how to loop through a file without losing anything, not the empty lines, not leading colon characters, not the special characters and hopefully accept lines up to 8191 characters long – Shodan Aug 14 '23 at 05:35
  • I just gave a shot at your command syntax using my "batchsample.bat" file here are the results https://pastebin.com/rLh13H13 – Shodan Aug 14 '23 at 05:36
  • Here is my "batchsample.bat" file, which is just one of my overgrown batch file, which the underlying purpose of my question, is to cut it up into more manageable sections https://pastebin.com/XYyd8VpM . – Shodan Aug 14 '23 at 05:39
  • Here is the file which contains my latest version of the functions which perform the underlying purpose https://pastebin.com/5tdJbLjd called AddEscapeCharacters-AND-GetRandomString.bat of current relevant are functions called :FindAllLabels-DEMO and :GetFunctionDefinition-DEMO – Shodan Aug 14 '23 at 05:40
  • I should really make another question, as this is far outside the scope of this question. The underlying problem is, take a large batch file, scan all lines and find the line number for 1. all function label name and their line number, line number of all empty lines, line number of all special label called :EndOf_functionname , and optional for function missing :EndOf_ loop back from the next function label, to the preceding empty line, and then find the first line containing goto :eof or exit /b – Shodan Aug 14 '23 at 05:43
  • The result will be a batch file that you call like this ListFunction.bat %batchfilename% returns a list of valid function names and ExtractFunction %batchfilename% %functionname% %outputbatchfile% which will create a new batch file containing only that function (it will also automatically add a function description preamble comment block, so every function will be documented, including automatically filling out the list of dependencies on other function ) – Shodan Aug 14 '23 at 05:45
  • And lastly, it will also add a SetMacro block, which will create macros for every call :examplefunction , which will insert a function like this if "[%call examplefunction%]" EQU "[]" if (where find examplefunction.bat in path) (set "call examplefunction=call examplefunction.bat" ) else ( set "call examplefunction=call :examplefunction" ) – Shodan Aug 14 '23 at 05:50
  • I think I am 80% done with this, using findstr to loop through all lines unmolested was a big stumbling block. I think my last hurdle is how to reliably leave endlocal without damaging string content but I think I have a solution with my FileToArray structure – Shodan Aug 14 '23 at 05:52
  • I am giving this another try, I made this "SimpleFileToArray" function https://pastebin.com/bvYMWqbj seemingly gets every line of the text file into the array. Doesn't use delayedexpansion so ! are not lost. I'm not getting errors either from special characters. But the question now is How do I remove the line numbers without errors or mangling characters ? Example of a line LinesArray[976]=976:976:::Usage Call :EnablePowerThrottling PackageName – Shodan Aug 18 '23 at 07:55
  • I thought I had a solution in this latest version https://pastebin.com/FvuP1hTP Unfortunately it does not work, only sets the first element and everything afterwards is lost. I created this debug version, which appears to work but doesn't. https://pastebin.com/uFdK2KsZ . Here is a sample of the output of the debug function https://pastebin.com/jgWfw1vM – Shodan Aug 20 '23 at 01:34
  • Worse, is that even if it did set the values. The exclamation marks are lost. Not just lost but expanded. For example Line number nine (minus the tabs) is in the original REM echo Power Scheme Name : %%f GUID : %%d !_powercfglist_default! and it becomes in the output REM echo Power Scheme Name : %%f GUID : %%d – Shodan Aug 20 '23 at 01:37
  • I am sorry, I can't follow your large comments without enough explanation nor bases. I don't know it you want to "list empty lines", or "fill array with file lines", etc because you have not posted an enough clear description of such topics. Besides, there are also _a lot_ of questions and answers about most of the details you put in your comments. Anyway, I posted a new code that _perhaps_ could aids you (see the EDIT in my code), but be aware that my method uses a _very different_ approach to solve the problem than yours... **`:(`** – Aacini Aug 20 '23 at 19:41
0

Based on @Compo's comment reference

for /f "delims=" %%i in ('%SystemRoot%\System32\findstr.exe /n /r "^" "%textfile%"') do (
  set _buffer=%%i
  setlocal enabledelayedexpansion
  set _buffer=!_buffer:*:=!
  call :IsLabel _return _buffer && ( action if IsLabel returns 0 ) || action if IsLabel returns nonzero )
  endlocal 
)

Extended answer

I suspect most people who wish to loop through all lines, including empty lines and not lose the first colon, are trying to list all labels in a batch file like me.

The solution to the exact question above is already states. But what follows is my current solution to the underlying problem

:FindAllLabels-DEMO

Call :ClearVariablesByPrefix FileLines OutputLabelArray _FALFFLA

Call :FileToArray FileLines "batchsample.bat"

Call :FindAllLabelsFromFileLineArray FileLines OutputLabelArray

Call :ClearVariablesByPrefix FileLines

GoTo :EOF

::Usage Call :FindAllLabelsFromFileLineArray FileLineArray OutputLabelArray
:FindAllLabelsFromFileLineArray
set "_FindAllLabelsFromFileLineArray_prefix=_FALFFLA"
set "_FALFFLA_Lines=%~1"
call set /a "_FALFFLA_ubound=%%%_FALFFLA_Lines%.ubound%%"
call set /a "_FALFFLA_index=%%%_FALFFLA_Lines%.lbound%%"
set "_FALFFLA_Output=%~2"
call set /a "_FALFFLA_output_lbound=%%%_FALFFLA_Output%.lbound%%" 2>nul
call set /a "_FALFFLA_output_ubound=%%%_FALFFLA_Output%.ubound%%" 2>nul
if not defined _FALFFLA_output_lbound set /a "_FALFFLA_output_lbound=0"
if not defined _FALFFLA_output_ubound set /a "_FALFFLA_output_ubound=-1"
:FindAllLabelsFromFileLineArray-loop
if %_FALFFLA_index%==2 echo %_FALFFLA_Lines%[%_FALFFLA_index%]
if %_FALFFLA_index%==2 call echo %%%_FALFFLA_Lines%[%_FALFFLA_index%]%%
Call :TrimBeforeChar %_FALFFLA_Lines%[%_FALFFLA_index%] : %_FALFFLA_Lines%[%_FALFFLA_index%]
Call :IsLabel _FALFFLA_IsLabel %_FALFFLA_Lines%[%_FALFFLA_index%]
if "[%_FALFFLA_IsLabel%]" EQU "[true]" set /a "_FALFFLA_output_ubound+=1" 
if "[%_FALFFLA_IsLabel%]" EQU "[true]" (
    set "%_FALFFLA_Output%[%_FALFFLA_output_ubound%]=%_FALFFLA_index%"
    Call :GetLabel %_FALFFLA_Output%[%_FALFFLA_output_ubound%].labelname %_FALFFLA_Lines%[%_FALFFLA_index%]
    )
if "[%_FALFFLA_IsLabel%]" EQU "[true]" call echo %%%_FALFFLA_Output%[%_FALFFLA_output_ubound%].labelname%%
echo falffla %_FALFFLA_index%
set /a "_FALFFLA_index+=1"
if %_FALFFLA_index% GTR 500 GoTo :FindAllLabelsFromFileLineArray-skip
if %_FALFFLA_index% LEQ %_FALFFLA_ubound% GoTo :FindAllLabelsFromFileLineArray-loop
:FindAllLabelsFromFileLineArray-skip
set /a "%_FALFFLA_Output%.lbound=%_FALFFLA_output_lbound%"
set /a "%_FALFFLA_Output%.ubound=%_FALFFLA_output_ubound%"
Call :ClearVariablesByPrefix %_FindAllLabelsFromFileLineArray_prefix% _FindAllLabelsFromFileLineArray
GoTo :EOF

::Usage Call :TrimBeforeChar OutputVar TrimChar InputVar
:TrimBeforeChar
call set %~1=%%%~3:*%~2=%%
GoTo :EOF

::Usage Call :IsLabel Output Input
:IsLabel
setlocal enabledelayedexpansion
set "TAB=   "
set "_IsLabel_Output=%~1"
set /a "_IsLabel_index=0"
REM echo input %~2
REM set _islabel
:IsLabel-loop
set "_IsLabel_char=!%~2:~%_IsLabel_index%,1!
REM echo input !%_IsLabel_Input%!
REM echo current char is i!_IsLabel_char!i name %_IsLabel_labelname%
if "[!_IsLabel_char!]" EQU "[]" GoTo :IsLabel-end
if "[!_IsLabel_char!]" EQU "[%TAB%]" ( if "[%_IsLabel_result%]" NEQ "[true]" ( set /a "_IsLabel_index+=1" & GoTo :IsLabel-loop ) else ( GoTo :IsLabel-end ) )
if "[!_IsLabel_char!]" EQU "[ ]" ( if "[%_IsLabel_result%]" NEQ "[true]" ( set /a "_IsLabel_index+=1" & GoTo :IsLabel-loop ) else ( GoTo :IsLabel-end ) )
if "[!_IsLabel_char!]" EQU "[:]" ( if "[%_IsLabel_result%]" NEQ "[true]" ( set /a "_IsLabel_index+=1" & set "_IsLabel_result=true" & GoTo :IsLabel-loop ) else ( GoTo :IsLabel-end ) )
if "[%_IsLabel_result%]" EQU "[true]" ( set /a "_IsLabel_index+=1" & set "_IsLabel_labelname=%_IsLabel_labelname%!_IsLabel_char!" & GoTo :IsLabel-end )
REM echo ending loop  current char is !_IsLabel_char! name %_IsLabel_labelname% islabel %_IsLabel_result%
:IsLabel-end
if "[%_IsLabel_labelname%]" NEQ "[]" ( set "_IsLabel_result=0" ) else ( set "_IsLabel_result=1" )
if "[%_IsLabel_result%]" EQU "[0]" ( set "_IsLabel_output_value=true" ) else ( set "_IsLabel_output_value=false" )
REM echo result %_IsLabel_result% name %_IsLabel_labelname%
endlocal & Call :ClearVariablesByPrefix _IsLabel & set "%_IsLabel_Output%=%_IsLabel_output_value%" & exit /b %_IsLabel_result%

::Usage Call :GetLabel Output Input
:GetLabel
setlocal enabledelayedexpansion
set "TAB=   "
set "_GetLabel_Output=%~1"
REM set _GetLabel_Input=%~2
set /a "_GetLabel_index=0"
REM echo input %~2
REM set _GetLabel
:GetLabel-loop
set "_GetLabel_char=!%~2:~%_GetLabel_index%,1!
REM echo current char is i!_GetLabel_char!i name %_GetLabel_labelname%
if "[!_GetLabel_char!]" EQU "[]" GoTo :GetLabel-end
if "[!_GetLabel_char!]" EQU "[%TAB%]" ( if "[%_GetLabel_result%]" NEQ "[true]" ( set /a "_GetLabel_index+=1" & GoTo :GetLabel-loop ) else ( GoTo :GetLabel-end ) )
if "[!_GetLabel_char!]" EQU "[ ]" ( if "[%_GetLabel_result%]" NEQ "[true]" ( set /a "_GetLabel_index+=1" & GoTo :GetLabel-loop ) else ( GoTo :GetLabel-end ) )
if "[!_GetLabel_char!]" EQU "[:]" ( if "[%_GetLabel_result%]" NEQ "[true]" ( set /a "_GetLabel_index+=1" & set "_GetLabel_result=true" & GoTo :GetLabel-loop ) else ( GoTo :GetLabel-end ) )
if "[%_GetLabel_result%]" EQU "[true]" ( set /a "_GetLabel_index+=1" & set "_GetLabel_labelname=%_GetLabel_labelname%!_GetLabel_char!" & GoTo :GetLabel-loop )
echo ending loop  current char is !_GetLabel_char! name %_GetLabel_labelname% GetLabel %_GetLabel_result%
:GetLabel-end
if "[%_GetLabel_labelname%]" NEQ "[]" ( set "_GetLabel_result=0" ) else ( set "_GetLabel_result=1" )
REM set _GetLabel
REM echo result %_GetLabel_result% name %_GetLabel_labelname%
endlocal & Call :ClearVariablesByPrefix _GetLabel & set "%_GetLabel_Output%=%_GetLabel_labelname%" & exit /b %_GetLabel_result%

::Usage Call :FileToArray OutputArray Filename
:FileToArray
set "_FileToArray_prefix=_FTA"
set "_FTA_Output=%~1"
call set "_FTA_Output_ubound=%%%~1.ubound%%"
if "[%_FTA_Output_ubound%]"=="[]" ( set "_FTA_Output_ubound=1" ) else ( set /a "_FTA_Output_ubound+=1" ) 
call set "_FTA_Output_lbound=%%%~1.lbound%%"
if "[%_FTA_Output_lbound%]"=="[]" set "_FTA_Output_lbound=1"
set /a "_FTA_initial_ubound=%_FTA_Output_ubound%"
shift
:FileToArray-arguments
if "[%~1]" EQU "[skip]" ( set "_FTA_skip=skip=%~2 " & shift & shift & GoTo :FileToArray-arguments )
if "[%~1]" EQU "[eol]" ( set "_FTA_eol=eol=%~2 " & shift & shift & GoTo :FileToArray-arguments )
if "[%~1]" EQU "[tokens]" ( set "_FTA_tokens=tokens=%~2 " & shift & shift & GoTo :FileToArray-arguments )
if "[%~1]" EQU "[delims]" ( set "_FTA_delimiters=delims=%~2" & shift & shift & GoTo :FileToArray-arguments )
if "[%~1]" EQU "[usebackquote]" ( set "_FTA_backquote=usebackq " & shift & GoTo :FileToArray-arguments )
if "[%_FTA_tokens%]" EQU "[]" set "_FTA_tokens=tokens=*"
setlocal enabledelayedexpansion
set _FTA_localscope=true
for /f "%_FTA_backquote%%_FTA_skip%%_FTA_eol%%_FTA_tokens%%_FTA_delimiters%" %%a in ('findstr /n "^" "%~1"') do (
    set %_FTA_Output%[!_FTA_Output_ubound!]=%%a 
    set /a "_FTA_Output_ubound+=1"
    )
if "[%_FTA_initial_ubound%]" NEQ "[%_FTA_Output_ubound%]" set /a "%_FTA_Output%.ubound=!_FTA_Output_ubound!-1"
if "[%_FTA_Output_lbound%]" NEQ "[]" set /a "%_FTA_Output%.lbound=!_FTA_Output_lbound!"
for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do ( endlocal & set "%%a" )
if defined _FTA_localscope endlocal
Call :ClearVariablesByPrefix %_FileToArray_prefix% _FileToArray
GoTo :EOF

:: Usage Call :ClearVariablesByPrefix myPrefix
:ClearVariablesByPrefix
if "[%~1]" NEQ "[]" for /f "tokens=1,2 delims==" %%a in ('set %~1 2^>nul') do set %%a=
if "[%~2]" NEQ "[]" shift & GoTo :ClearVariablesByPrefix
GoTo :EOF

The output of these functions will be an array that lists the line numbers and label names for each label as follows from sample test run below

OutputLabelArray.lbound=0
OutputLabelArray.ubound=47
OutputLabelArray[0]=2
OutputLabelArray[0].labelname=setup
OutputLabelArray[10]=156
OutputLabelArray[10].labelname=EchoArguments
OutputLabelArray[11]=176
OutputLabelArray[11].labelname=EchoArgumentsEND
OutputLabelArray[12]=182
OutputLabelArray[12].labelname=ArgumentsToArray
OutputLabelArray[13]=184
OutputLabelArray[13].labelname=ArgumentsToArrayMAIN
OutputLabelArray[14]=212
OutputLabelArray[14].labelname=ArgumentsToArrayEND
OutputLabelArray[15]=219
OutputLabelArray[15].labelname=QUIT
OutputLabelArray[16]=225
OutputLabelArray[16].labelname=Sleep
OutputLabelArray[17]=230
OutputLabelArray[17].labelname=ClearLocalVariables
OutputLabelArray[18]=235
OutputLabelArray[18].labelname=ClearVariablesByPrefix
OutputLabelArray[19]=240
OutputLabelArray[19].labelname=GoToFolderOfBatchFile
OutputLabelArray[1]=24
OutputLabelArray[1].labelname=Constants
OutputLabelArray[20]=244
OutputLabelArray[20].labelname=ReturnToInitialFolder
OutputLabelArray[21]=249
OutputLabelArray[21].labelname=ShowCodepage
OutputLabelArray[22]=254
OutputLabelArray[22].labelname=GetCodePage
OutputLabelArray[23]=260
OutputLabelArray[23].labelname=SetCodePage
OutputLabelArray[24]=266
OutputLabelArray[24].labelname=true
OutputLabelArray[25]=272
OutputLabelArray[25].labelname=false
OutputLabelArray[26]=283
OutputLabelArray[26].labelname=deconcatenate
OutputLabelArray[27]=291
OutputLabelArray[27].labelname=deconcatenate_input_loop
OutputLabelArray[28]=297
OutputLabelArray[28].labelname=deconcatenate_delimiter_loop
OutputLabelArray[29]=312
OutputLabelArray[29].labelname=DeconcatenateFast
OutputLabelArray[2]=26
OutputLabelArray[2].labelname=Macros
OutputLabelArray[30]=329
OutputLabelArray[30].labelname=GetLastElement
OutputLabelArray[31]=336
OutputLabelArray[31].labelname=GetLastElement_input_loop
OutputLabelArray[32]=342
OutputLabelArray[32].labelname=GetLastElement_delimiter_loop
OutputLabelArray[33]=355
OutputLabelArray[33].labelname=GetLastElementFast
OutputLabelArray[34]=366
OutputLabelArray[34].labelname=GetNthElement
OutputLabelArray[35]=374
OutputLabelArray[35].labelname=GetNthElement_input_loop
OutputLabelArray[36]=380
OutputLabelArray[36].labelname=GetNthElement_delimiter_loop
OutputLabelArray[37]=397
OutputLabelArray[37].labelname=GetNthElementFast
OutputLabelArray[38]=412
OutputLabelArray[38].labelname=GetNthElementFast_ExitFor
OutputLabelArray[39]=419
OutputLabelArray[39].labelname=GetPreviousElement
OutputLabelArray[3]=56
OutputLabelArray[3].labelname=main
OutputLabelArray[40]=424
OutputLabelArray[40].labelname=GetNextElement
OutputLabelArray[41]=429
OutputLabelArray[41].labelname=DeleteElement
OutputLabelArray[42]=438
OutputLabelArray[42].labelname=AddElement
OutputLabelArray[43]=446
OutputLabelArray[43].labelname=EchoArray
OutputLabelArray[44]=461
OutputLabelArray[44].labelname=EchoArray-internal-loop
OutputLabelArray[45]=478
OutputLabelArray[45].labelname=DereferenceArrayToArray
OutputLabelArray[46]=493
OutputLabelArray[46].labelname=DereferenceArrayToArray-output-loop
OutputLabelArray[47]=495
OutputLabelArray[47].labelname=DereferenceArrayToArray-suffix-loop
OutputLabelArray[4]=89
OutputLabelArray[4].labelname=BASICDEMO
OutputLabelArray[5]=104
OutputLabelArray[5].labelname=END
OutputLabelArray[6]=117
OutputLabelArray[6].labelname=ElevateAndWait
OutputLabelArray[7]=119
OutputLabelArray[7].labelname=Elevate
OutputLabelArray[8]=137
OutputLabelArray[8].labelname=IsAdmin
OutputLabelArray[9]=146
OutputLabelArray[9].labelname=HELP
Shodan
  • 1,065
  • 2
  • 13
  • 35
  • Once again for /f is a great disappointment ! – Shodan Aug 12 '23 at 03:04
  • This is exquisitely slow and fragile, but as you can see, it works – Shodan Aug 12 '23 at 04:31
  • Thanks for the corrections. I am really struggling to edit variable that contain poisonous characters out of a nested setlocal. I realized that my trick in FileToArray, using the set command, will remove leading "=" characters. for /F "delims=" %%a in ('set %_FTA_Output% 2^>nul') do ( endlocal & set "%%a" ). I think it would also break on poison characters inside quotes. Is there a trick ? All I can think it to escape every character before endlocal ? – Shodan Aug 13 '23 at 21:28
  • Disregard previous comment regarding the for loop, I don' t understand how that is possible, but it doesn't actually rip out the leading "=", I just tested. Wait was it because I was using tokens= all along ?! – Shodan Aug 13 '23 at 21:41
  • AH, I figured out my confusion, for /F "delims=" is no delimiters, I mistakenly read it as delimiter = "=" – Shodan Aug 13 '23 at 21:45