Question

I want to match all lines of the following text with FINDSTR /R

LABO_A =
  (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))
    (CONNECT_DATA =
      (SERVICE_NAME = LABO)
    )
  )

I already tried What are the undocumented features and limitations of the Windows FINDSTR command? Especially the "Searching across line breaks" part. But unfortunately it didn't work.

My approach is the following:

SETLOCAL
set LF=^


FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"

SETLOCAL enableDelayedExpansion
FINDSTR /R "LABO_A.=.!CR!*!LF!.*(DESCRIPTION.=.!CR!*!LF!.*(ADDRESS.=.(PROTOCOL.=.TCP)(HOST.=.host01)(PORT.=.1521))!CR!*!LF!.*(CONNECT_DATA.=!CR!*!LF!.*(SERVICE_NAME.=.LABO)!CR!*!LF!.*)!CR!*!LF!.*)" %FINDPATH%

Am I missing something? Or is the batch regex simply not powerful enough to realize this?

SOLUTION: The approach of @dbenham let me reconsider my regex-string. So I edited it to

FINDSTR /R /C:"LABO_A =!CR!*!LF!.*(DESCRIPTION =!CR!*!LF!.*(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!CR!*!LF!.*(CONNECT_DATA =!CR!*!LF!.*(SERVICE_NAME = LABO)!CR!*!LF!.*)!CR!*!LF!.*)" %FINDPATH% > NUL

I removed some unnecessary white spaces and adapted the parameters of FINDSTR.

Now it works.

Was it helpful?

Solution

Your regex is wrong. Your source lines end immediately after the =, but the extra . in your regex is looking for an additional character after the =.

It looks to me you are using . to represent white space. I think you would be better off using actual spaces, but then you need the /C option.

The following matches the lines successfully.

@echo off
SETLOCAL
set LF=^


FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"

SETLOCAL enableDelayedExpansion
FINDSTR /R /C:"LABO_A =!CR!*!LF! *(DESCRIPTION =!CR!*!LF! *(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!CR!*!LF! *(CONNECT_DATA =!CR!*!LF! *(SERVICE_NAME = LABO)!CR!*!LF! *)!CR!*!LF! *)" test.txt

Note that even though all lines in the regex are matched, only the first line of the matching set is printed.

I suspect that the line breaks are not required in your configuration file. Here is another variation that allows for more variation in the white space.

@echo off
setlocal enableDelayedExpansion
set LF=^


FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"
set "ws=[ !cr!!lf!]*"

FINDSTR /RX /C:"LABO_A =!ws!(DESCRIPTION =!ws!(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!ws!(CONNECT_DATA =!ws!(SERVICE_NAME = LABO)!ws!)!ws!)!ws!" test.txt

I also attempted to allow white space in every place I thought possible, but that exceeded FINDSTR's maximum REGEX string length.

OTHER TIPS

Essentially, batch regex isn't powerful enough. SED would be better no doubt.

Nonetheless, here's a way to detect that a sequence of lines appears in a file. It's a little restricted, but should suffice for the sequence you've nominated. It assumes that leading spaces are not significant.

@ECHO OFF
SETLOCAL enabledelayedexpansion
FOR /f "delims==" %%a IN ('set l_ 2^>nul') DO "SET %%a="
SET /a lines=0
FOR /f "tokens=*" %%a IN (q19859936.txt) DO SET /a lines+=1&SET l_!lines!=%%a

SET hits=0
SET "stop="
FOR /f "tokens=*" %%a IN (q19859936.test) DO (
 SET l_0=%%~a
 CALL :test
 IF DEFINED stop GOTO done
)
:done
IF DEFINED stop (ECHO FOUND ) ELSE (ECHO NOT FOUND)

GOTO :EOF

:test
SET /a hits+=1
ECHO IF NOT "!l_%hits%!"=="%l_0%" 
IF NOT "!l_%hits%!"=="%l_0%" SET hits=0&IF %hits%==1 (GOTO :eof) ELSE (GOTO test)
IF %hits%==%lines% SET stop=Y
GOTO :eof

[edited code 20131111T1408Z - first FOR had tokens=2]

The initial FOR ensures that variables L_* are cleared.

The file q19859936.txt is read as the line-sequence-to-be-detected data.

q19859936.test is then examined. Each line is assigned to L_0 in turn and the internal subroutine :test will check to see whether it matches the next-line-expected.

The IF NOT statement is significant - and seemingly illogical (you'd need to add the /i switch to make it case-insensitive if you so want...) When batch parses the line, %hits% is replaced by the then-current value of hits and THEN the line is executed, so hits will be reset to 0 if ever a mismatch is found. If the HITS count WAS not 1, then the test is repeated. This takes care of the case

matches line 1
matches line 2
matches line 3
matches line 1
matches line 2
matches line 3
matches line 4
matches line 5
matches line 6

where the second "line 1" is encountered when "line 4" was expected. HITS is thus changed to 0, but it WAS 4 so execution passes back to :test and the test repeated with HITS=1.

Another approach could have been to read lines into another array (say L#*) and test that L_* matched L#*, for %LINES% entries. On no match, ripple-up and assign the next line read to L#!lines! ... but I thought of that later. Probably be easier and better, too - I'll leave it as an exercise for whoever may be interested.

This will work if you are after the LABO_A reference.

It uses a helper batch file called findrepl.bat from - https://www.dropbox.com/s/rfdldmcb6vwi9xc/findrepl.bat

Place findrepl.bat in the same folder as the batch file or on the path.

type "file.txt" | findrepl "^LABO_A =" /e:"^  \)"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top