Believer
Believer

Reputation: 95

Loop recursively into subfolders and look for a substring into files

I would like to create a script that loops reccursively through subfolders of D:\MyFolder\ for example, to find multiple files named MyFile.txt then look into each file for the keyword FROM and retrieve the string between the FROM and the next semicolon ;.

Sample of MyFile.txt:

LOAD
   Thing1,
   Thing2,
   Thing3,
FROM
   Somewhere ;

The desired result is: Somewhere.

(The position of the semicolon ; can be in another line).

I did some tries but I did not succeed in writing a correct script:

@echo off
SET PATH="D:\MyFolder\"
FOR /R %PATH% %%f IN (MyFile.txt) DO (
    FOR /F "delims=FROM eol=;" %%A in (%%f) do (
    set str=%%A
    ECHO %str% 
    )
)

If it can't be done in batch, please let me know in which language I can do it easily. I would like to have an executable script in the end.

Upvotes: 0

Views: 238

Answers (1)

aschipfl
aschipfl

Reputation: 34909

There are some issues in your code:

  • The delims option of for /F defines characters but not words to be used as delimiter for parsing text files. To find a word, use findstr instead (you could use its /N option to derive the position/line number of the search string).
  • The eol option of for /F defines a character to ignore a line in case it occurs at the beginning (or it is preceded by delimiters only).
  • for /R does actually not search for files in case there are no wild-cards (?, *) in the set (that is the part in between parentheses). The dir /S command does, so you can work around this by wrapping a for /F loop around dir /S.
  • The PATH variable is used by the system to find executables, like findstr, so you must not overwrite it; use a different variable name instead.

Here is the way I would probably do it (supposing any text following the keyword FROM needs to be returned also):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=D:\MyFolder" & rem // (root directory of the tree to find files)
set "_FILE=MyFile.txt"  & rem // (name of the files to find in the tree)
set "_WORD=FROM"        & rem // (keyword to be searched within the files)
set "_CHAR=;"           & rem // (character to be searched within the files)

rem // Walk through the directory tree and find matching files:
for /F "delims=" %%F in ('dir /B /S "%_ROOT%\%_FILE%"') do (
    rem // Retrieve the line number of each occurrence of the keyword:
    for /F "delims=:" %%N in ('findstr /N /I /R "\<%_WORD%\>" "%%~F"') do (
        rem // Process each occurrence of the keyword in a sub-routine:
        call :PROCESS "%%~F" %%N
    )
)

endlocal
exit /B


:PROCESS
rem // Ensure the line number to be numeric and build `skip` option string:
set /A "SKIP=%~2-1"
if %SKIP% GTR 0 (set "SKIP=skip^=%SKIP%") else set "SKIP="
rem // Read file starting from line containing the found keyword:
set "FRST=#"
for /F usebackq^ %SKIP%^ delims^=^ eol^= %%L in ("%~1") do (
    set "LINE=%%L"
    setlocal EnableDelayedExpansion
    rem // Split off everything up to the keyword from the first iterated line:
    if defined FRST set "LINE=!LINE:*%_WORD%=!"
    rem /* Split read line at the first occurrence of the split character;
    rem    the line string is augmented by preceding and appending a space,
    rem    so it is possible to detect whether a split char. is there: */
    for /F "tokens=1,* delims=%_CHAR% eol=%_CHAR%" %%S in (" !LINE! ") do (
        endlocal
        set "TEXT=%%S"
        set "RMND=%%T"
        set "ITEM=%~1"
        setlocal EnableDelayedExpansion
        rem // Check whether a split character is included in the line string:
        if not defined RMND (
            rem // No split char. found, so get string without surrounding spaces:
            set "TEXT=!TEXT:~1,-1!"
        ) else (
            rem // Split char. found, so get string without leading space:
            set "TEXT=!TEXT:~1!"
        )
        rem // Trimm leading white-spaces:
        for /F "tokens=*" %%E in ("!TEXT!") do (
            endlocal
            set "TEXT=%%E"
            setlocal EnableDelayedExpansion
        )
        rem // Return string in case it is not empty:
        if defined TEXT echo(!ITEM!;!TEXT!
        rem // Leave sub-routine in case split char. has been found:
        if defined RMND exit /B
    )
    endlocal
    set "FRST="
)
exit /B

Upvotes: 1

Related Questions