1

first off I haven never really used batch but I'm trying to accomplish something and start to feel overwhelmed.  I have a bunch of *txt files in Subdirectories. I want to search these *txt files for if they don't contain a certain string (ABCDEF) and output the results into a new file. 

If a file doesn't contain ABCDEF: 

output the Filepath and Name as well as the content of every line that begins with "[". 

If a file contains ABCDEF one or more times: 

Search between lines beginning with "[" for ABCDEF if not found output the first of the two lines beginning with "[" and move like that through the whole file. 

Example 

My files look like this: 

[ID1]
  text not containing abcdef
  [ID2]
  text containing abcdef
  [ID3]
  text not containing abcdef 

Here my sample output should be: 

Filename
  [ID1]
  [ID3] 


Additional clarification of requirements

I have a bunch of AD User Group Logs in different folders. These are exported at random times and saved as *.txt. These Files contain from just a few up to a few hundred lines and are all set up the same way (as displayed above).

What my idea in terms of pseudocode is, is the following:

Loop through all folders and subfolders
Go through the files file by file
var output    
var searchString = abcdef    
var currentfile = Current File Name/Path
While nextline != End of File{
    If currentline[0] = "["
    output = currentline
    While nextline[0] != "["
    If currentline.contains(searchString)
        If currentfile != 0{
        echo currentfile
        }    
    echo output
    currentfile = 0
    break
    else
    continue
    }

So pretty much: look for searchstring, if you find: output the last line before that began with "[" and then keep looking


To add what I have gotten so far: I managed to get the files containing the name and getting the first GUID since it is always the first line.
Sadly /v didn't work since as soon as a line doesn't contain the searchstring it evaluates as true.

Edit I got it so far as I now am able to get all files that don't contain my search string by replacing && with ||. Now if I get it to output all lines beginning with [ in those files I'm halfway there Done. Now all I have to find is a way to search the blocks between the lines beginning with "[" and return the values if nothing is found. Maybe someone has a tip? ^^

This is my code so far:

@echo off & setlocal
set "SrcDir=my root directory"
set "FileType=txt"
set "SearchKey=abcdef"
set "LogFile=output.log"
set linenum=1

(for /R %%f in ("*.%FileType%") do (
type "%%~f" | findstr /c:"%SearchKey%" > NUL || (
echo %%~f & for /f "delims=" %%a in ('findstr /r /b /c:"\[" "%%~f"') do echo %%a
)
)) > "%LogFile%"
Rob
  • 25
  • 4
  • Welcome to stackoverflow. What have you tried so far? – Paulo Scardine Sep 06 '17 at 13:42
  • I have only gotten to work to find the string and output the first line; I tried searching for files not containing the string with the parameter /v but it just gives me all the files: `(for /R %%f in ("*.%FileType%") do ( type "%%~f" | findstr /V /c:"%SearchKey%" > NUL && ( echo %%~f & type "%%f"|findrepl /o:%linenum%:%linenum% ) )) > "%LogFile%"` – Rob Sep 06 '17 at 14:05
  • Thanks for improving the question! – Paulo Scardine Sep 06 '17 at 14:20

2 Answers2

0

can be done with a "flag"

@echo off
setlocal EnableDelayedExpansion
set searchkey=abcdef
set flag=1
for /r %%f in (*.txt) do (
  echo %%f
  set "key="
  for /f "usebackq delims=" %%a in ("%%f") do (
    echo %%a|find "[" >nul && (
      if not defined flag if defined key echo  !key!
      set "key=%%a"
      set "flag="
    )
    echo %%a|find "%searchkey%" >nul && set flag=1
  )
  if not defined flag if defined key echo  !key!
)

key remembers the last [something] line

For the && and || thingy, see SS64

For the !variable! syntax see delayed expansion

Stephan
  • 53,940
  • 10
  • 58
  • 91
  • You have some logic issues. `key` must be cleared before each inner loop. `key` should not be echoed if it is not defined. And the last `echo !key!` should only fire if `flag` is not defined. – dbenham Sep 06 '17 at 19:54
  • @debenham: will check and edit - but not tonight anymore. – Stephan Sep 06 '17 at 20:21
  • didn' let me sleep... :=) – Stephan Sep 06 '17 at 20:32
  • Thank you for your answer :)! Now I understand your logic and it's the same way I thought I could get it done. Currently I have some trouble with the script: It just outputs all the file paths+ names, but not the IDs. Also it throws back an error in cmd stating `"The File C:\Mypath\NextpathhereItStopsAtSpace could not be found` It seems to miss a delimiter somewhere, since it stops reading the filepath at the first "Space" it encounters. – Rob Sep 07 '17 at 08:13
  • I managed to get it to work with dbenham's JREPL solution. Still thank you so much for your help and effort. You went exactly the route I wanted to go but couldn't figure out. – Rob Sep 07 '17 at 08:46
  • added support for filenames with spaces. Dbenhams JREPL solution is probably the best and was my first idea, but I'm not yet firm enough with REGEX to come up with a working solution myself. – Stephan Sep 07 '17 at 16:54
0

Disregarding the outer FOR loop, this can be done very efficiently with nothing but FINDSTR :) !

The key is a little known FINDSTR capability to search across line breaks (it functions somewhat like a regular expression look-ahead). I describe the feature at the bottom of my answer to What are the undocumented features and limitations of the Windows FINDSTR command?.

@echo off
setlocal

set "SrcDir=my root directory"
set "FileType=txt"
set "SearchKey=abcdef"
set "LogFile=output.log"

(set LF=^
%=This creates a linefeed character=%
)

(
  for /r "%SrcDir%" %%F in ("*.%FileType%") do (
    echo %%~fF
    cmd /v:on /c findstr /rvc:"!LF!.*%SearchKey%" "%%F"|findstr /b "["
    echo(
  )
) >"%LogFile%"

I solve the problem in two steps. The first FINDSTR returns all lines that do NOT precede a line containing your search key. That result is piped into a second FINDSTR that returns only lines that begin with [.

Each side of the pipe is executed in a new cmd.exe process with delayed expansion disabled. But the LF variable must be expanded using delayed expansion. That is why I explicitly execute the left side in a new cmd.exe session with delayed expansion enabled via /v:on.

Another option is my JREPL.BAT regular expression text processing utility. It is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward - no 3rd party exe file required.

I only need a single call to JREPL for each file.

@echo off
setlocal

set "SrcDir=my root directory"
set "FileType=txt"
set "SearchKey=abcdef"
set "LogFile=output.log"

set "find=^\[[^\r\n]*$(?!\r?\n[^\r\n]*%SearchKey%)"
set "repl="

(
  for /r "%SrcDir%" %%F in ("test*.%FileType%") do (
    echo %%~fF
    call jrepl find repl /v /m /match /f "%%F"
    echo(
  )
) >"%LogFile%"

If I got the requirements wrong, and all lines below [id] should be searched until the next [id], then the FINDSTR solution cannot be modified to work. But the JREPL search is easily modified to extend the search until the next [id] by simply changing the definition of FIND:

set "find=^\[[^\r\n]*$(?!(?:\n(?!\[)|.)*%SearchKey%)"
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • doesn't work correct if the searchkey isn't the first line of the section. – Stephan Sep 06 '17 at 20:16
  • @Stephan - That is how I interpreted the requirements. If the search must continue until the next `[` line, then FINDSTR by itself will not work. – dbenham Sep 06 '17 at 20:23
  • Thank you for your effort. I did get your jrepl.bat script and will look into it. Yes, Stephan is correct. The search should look for the string "between" lines beginning with [. I will clear up my requirements, sorry about that. – Rob Sep 07 '17 at 08:04
  • WOW. Your solution with JREPL worked perfectly. THANK you so much @dbenham I'm going to try to reproduce the logic behind your JREPL solution and will get back to you if I have a question and it is alright with you? – Rob Sep 07 '17 at 08:46