steve_o
steve_o

Reputation: 1243

Powershell - reading ahead and While

I have a text file in the following format:

.....
ENTRY,PartNumber1,,, 
FIELD,IntCode,123456
...
FIELD,MFRPartNumber,ABC123,,,
...
FIELD,XPARTNUMBER,ABC123
...
FIELD,InternalPartNumber,3214567
...
ENTRY,PartNumber2,,,
...
...

the ... indicates there is other data between these fields. The ONLY thing I can be certain of is that the field starting with ENTRY is a new set of records. The rows starting with FIELD can be in any order, and not all of them may be present in each group of data.

  1. I need to read in a chunk of data
  2. Search for any field matching the string ABC123
  3. If ABC123 found, search for the existence of the InternalPartNumber field & return that row of data.

I have not seen a way to use Get-Content that can read in a variable number of rows as a set & be able to search it.

Here is the code I currently have, which will read a file, searching for a string & replacing it with another. I hope this can be modified to be used in this case.

$ftype = "*.txt"
$fnames = gci -Path $filefolder1 -Filter $ftype -Recurse|% {$_.FullName}
$mfgPartlist = Import-Csv -Path "C:\test\mfrPartList.csv"

foreach ($file in $fnames) {
   $contents = Get-Content -Path $file
   foreach ($partnbr in $mfgPartlist) {
        $oldString = $mfgPartlist.OldValue
        $newString = $mfgPartlist.NewValue
        if (Select-String -Path $file -SimpleMatch $oldString -Debug -Quiet) {
           $stringData = $contents -imatch $oldString
           $stringData = $stringData -replace "[\n\r]","|"
           foreach ($dataline in $stringData) {
                $file +"|"+$stringData+"|"+$oldString+"|"+$newString|Out-File "C:\test\Datachanges.txt" -Width 2000 -Append
               }
           $contents = $contents -replace $oldString $newString
           Set-Content -Path $file -Value $contents
         }
   }
}

Is there a way to read & search a text file in "chunks" using Powershell? Or to do a Read-ahead & determine what to search?

Upvotes: 0

Views: 165

Answers (2)

mjolinor
mjolinor

Reputation: 68273

Assuming your fine isn't too big to read into memory all at once:

$Text = Get-Content testfile.txt -Raw

($Text -split '(?ms)^(?=ENTRY)') |
 foreach { 
  if ($_ -match '(?ms)^FIELD\S+ABC123')
   {$_ -replace '(?ms).+(^Field\S+InternalPartNumber.+?$).+','$1'}
}

FIELD,InternalPartNumber,3214567

That reads the entire file in as a single multiline string, and then splits it at the beginning of any line that starts with 'ENTRY'. Then it tests each segment for a FIELD line that contains 'ABC123', and if it does, removes everything except the FIELD line for the InternalPartNumber.

Upvotes: 1

Matt
Matt

Reputation: 46710

This is not my best work as I have just got back from vacation. You could use a while loop reading the text and set an entry flag to gobble up the text in chunks. However if your files are not too big then you could just read up the text file at once and use regex to split up the chunks and then process accordingly.

$pattern = "ABC123"
$matchedRowToReturn = "InternalPartNumber"
$fileData = Get-Content "d:\temp\test.txt" | Where-Object{$_ -match '^(entry|field)'} | Out-String
$parts = $fileData | Select-String '(?smi)(^Entry).*?(?=^Entry|\Z)' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
$parts | Where-Object{$_ -match $pattern} | Select-String "$matchedRowToReturn.*$" | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value

What this will do is read in the text file, drop any lines that are not entry or field related, as one long string and split it up into chunks that start with lines that begin with the work "Entry".

Then we drop those "parts" that do not contain the $pattern. Of the remaining that match extract the InternalPartNumber line and present.

Upvotes: 0

Related Questions