Lumpi
Lumpi

Reputation: 2821

Extract found line + subsequent line from a text file

Im trying to write a batch file which scans several files and looks for the string "WARNING". So far I have:

FORFILES -m*.f08 -C"CMD /C FINDSTR /N "WARNING"  ECHO @FILE >> ListOfWARNINGS

Problem one: It does not print each found string in a new line.

Problem two: How can I change the code and add a IF statement so that it also prints the line following the line where WARNING was found.

Upvotes: 2

Views: 2600

Answers (3)

dbenham
dbenham

Reputation: 130819

The Alex K solution works great, and performs well as long as the files are relatively small. But if the files become big, performance can become a major problem.

I set up a test with 4 files - 3 small files less then 1Kbyte, plus 1 large ~1Mbyte file. WARNINGs were scattered through the files, including near the beginning and end of the large file.

The Alex K solution took 4.75 minutes to complete! The problem is the entire result set of the FINDSTR within the FOR IN() must be cached, and Windows does this very inefficiently with large data sets.

Also, the solution has a potential problem in that it will corrupt any file names and/or data lines that contain ! because of delayed expansion. This can be fixed by toggling delayed expansion on and off appropriately, also taking care to preserve variable values across ENDLOCAL border as needed.


It is much faster to write the results to a temporary file and then use FOR /F to read the temporary file. I modified the code to use a temp file (not shown), and the time was cut to 43 seconds.


But there is still a much faster solution. The FOR loop is extremely slow at reading a file when compared to the speed of FINDSTR. Below is a solution that uses FINDSTR to search each file once for every warning, plus 2 more times. It seems like a lot of extra work, but the time to complete my test case is cut to 1 second!

This solution also eliminates any problems due to ! in either file names or file content.

The solution is optimized for cases where the number of WARNINGs is relatively small compared to the number of lines within the file. If the number of WARNINGs begins to approach the number of lines in the file, then it will actually become slower than the middle solution described above.

This solution also prefixes each warning with the name of the file and the line number.

@echo off
setlocal disableDelayedExpansion
set searchFiles="*.f08"
set outFile="ListOfWARNINGS"
set tempFile="%temp%\warnings%random%.txt"
set tempFile2="%temp%\warnings2_%random%.txt"
(
  for /f "tokens=1,2 delims=:" %%A in ('findstr /m WARNING %searchFiles% nul') do (
    findstr /n "^" "%%A" nul >%tempFile%
    echo(>>%tempFile%
    set "file=%%A"
    set "next=0"
    setlocal enableDelayedExpansion
    findstr /n WARNING "!file!" >%tempFile2%
    for /f "usebackq delims=:" %%N in (%tempFile2%) do (
      if %%N==!next! (set "current=") else set current=/c:"!file!:%%N:"
      set /a "next=%%N+1"
      findstr /bi !current! /c:"!file!:!next!:" %tempFile%
    )
    endlocal
  )
)>%outFile%
del %tempFile% 2>nul
del %tempFile2% 2>nul

The /I option in the last FINDSTR logically should not be necessary. It is there to protect against a bug where FINDSTR sometimes has problems with multiple literal search strings of different lengths.

Upvotes: 1

Alex K.
Alex K.

Reputation: 175748

I think that for getting the next line your actually going to have to loop the files & content as neither forfiles nor find support this as far as I know;

@echo off
setlocal enabledelayedexpansion
for %%f in (*.txt) do (
    echo Search %%f
    for /f "usebackq eol= delims=] tokens=1*" %%l in (`find /n /v "" "%%f"`) do (
        set line=%%m
        if !printnext!==1 (
            echo.%%m >> ListOfWARNINGS
            set printnext=0
        ) else (
            if not [!line!]==[] if not !line!==!line:WARNING=X! (
                echo %%m >> ListOfWARNINGS
                set printnext=1
            ) else (
                set printnext=0
            )
        )
    )
)

Here if not !line!==!line:WARNING=X! detects a match by asking if a line is equal to itself after WARNING is replaced with X.

C:\null>type a.txt

line 1
M O D E L S U M M A R Y
WARNING XXXXXXX
Line after warning
NUMBER OF CBAR ELEMENTS = 18655
lorem ipsum lorem ipsum lorem ipsum
WARNING ZZZZZZZ


C:\null>x.bat
Search a.txt

C:\null>type ListOfWARNINGS
WARNING XXXXXXX
Line after warning
WARNING ZZZZZZZ

C:\null>

Upvotes: 2

Siva Karuppiah
Siva Karuppiah

Reputation: 1013

Try like this ,

@ECHO OFF
:TOP
  IF (%1)==() GOTO END
  ECHO Value is "%1" and still running...
 SHIFT
 GOTO TOP
:END

And See this link also

Upvotes: 1

Related Questions