0

I have a script that seems to work correctly only it works to good. I have files that contain multiple lines with the string "PROCEDURE DIVISION.", with the period at the end.

What I need to do...

ONLY remove the [2nd occurrence] of the string "PROCEDURE DIVISION." if it's in the text file twice and bypass the file if it is only found once. I need to preserve the 1st occurrence and change/remove the 2nd occurrence.

I can find and replace all the occurrences easily, I have no clue how to replace only 1 of 2.

Is this possible using Powershell?

Here is my code so far...

Get-ChildItem 'C:\Temp\*.cbl' -Recurse | ForEach {#
     (Get-Content $_ | ForEach   { $_ -replace "PROCEDURE DIVISION\.", "                   "}) | Set-Content $_ 
} 

UPDATE

I got this to work and it's not pretty.

The only problem is is is capturing the string in the comments section. What I need to do is only count the string as a hit when it's found starting in position 8 on each line.

Is that possible?

Get-ChildItem 'C:\Thrivent\COBOL_For_EvolveWare\COBOL\COBOL\*.*' -Recurse | ForEach {
     ($cnt=(Get-Content $_ | select-string -pattern "PROCEDURE DIVISION").length)
     if ($cnt -gt "1") {
        (Get-Content $_ | ForEach   { $_ -replace "PROCEDURE DIVISION\.", "                   "}) | Set-Content $_
           $FileName = $_.FullName
           Write-Host "$FileName = $cnt" -foregroundcolor green
      } 
user3166462
  • 171
  • 9
  • Does this answer your question? [Replacing only the first occurrence of a word in a string](https://stackoverflow.com/questions/40089631/replacing-only-the-first-occurrence-of-a-word-in-a-string) – Andrew Ryan Davis Jan 11 '21 at 18:29
  • Hi Andrew! That is an awesome post but it does not solve my problem. After reading the other post I now realize I should have been very specific. I need to remove the 2nd occurrence of the string and leave the first one alone. Is that possible? Regards, -Ron – user3166462 Jan 11 '21 at 18:45

4 Answers4

0

This maybe a bit of a hack, but it works. $myMatches = $pattern.Matches in the case below gives us 3 matches, $myMatches[1].Index is the position of the second occurrence of the string you want to replace.

$text = "Hello foo, where are you foo? I'm here foo."

[regex]$pattern = "foo"

$myMatches =  $pattern.Matches($text)

if ($myMatches.count -gt 1)
{
  $newtext = $text.Substring(0,$myMatches[1].Index) + "bar" + $text.Substring($myMatches[1].Index + "foo".Length)

  $newtext
}
Peter Hahndorf
  • 10,767
  • 4
  • 42
  • 58
0

try this:

$Founded=Get-ChildItem 'C:\Temp\' -Recurse -file -Filter "*.cbl" | Select-String -Pattern 'PROCEDURE DIVISION.' -SimpleMatch | where LineNumber -GT 1 | select Path -Unique
$Founded | %{

$Nb=0
$FilePath=$_.Path

$Content=Get-Content $FilePath | %{
    if($_ -like '*PROCEDURE DIVISION.*')
    {
           $Nb++

           if ($Nb -gt 1)
           {
                $_.replace('PROCEDURE DIVISION.', '')
           }
           else
           {
                $_
           }
    }
    else
    {
      $_
    }


}

$Content | Set-Content -Path $FilePath

}
Esperento57
  • 16,521
  • 3
  • 39
  • 45
0

You could use switch for this:

Get-ChildItem -Path 'C:\Temp' -Filter '*.cbl' -File -Recurse | ForEach-Object {
    $occurrence     = 0
    $contentChanged = $false
    $newContent = switch -Regex -File $_.FullName {
        'PROCEDURE DIVISION\.' { 
            $occurrence++
            if ($occurrence -eq 2) {
                $_ -replace 'PROCEDURE DIVISION\.', "                   "
                $contentChanged = $true
            }
            else { $_ }
        }
        default { $_ }
    }
    # only rewrite the file if a change has been made
    if ($contentChanged) {
        Write-Host "Updating file '$($_.FullName)'"
        $newContent | Set-Content -Path $_.FullName -Force
    }
}
Theo
  • 57,719
  • 8
  • 24
  • 41
  • Hey Theo, This works! Thanks. I unfortunately have hit another snag. I need to now search and replace by position 7. I'm finding the String Procedure Division in the comments section now and it is Throwing off the accuracy of the script. Is it possible to search for the String ONLY in position 7 of each line? – user3166462 Jan 11 '21 at 20:17
  • @user3166462 Does it still have to be the 2nd occurrence, counting only if the string is found starts at position 7? In that case change the regex directly underneath the switch command to `'^.{6}PROCEDURE DIVISION\.'` – Theo Jan 11 '21 at 21:13
0

There are potential issues with all of the provided answers. Reading a file using switch statement is likely going to be the fastest method. But it needs to take into account PROCEDURE DIVISION. appearing multiple times on the same line. The method below will be more memory intensive than using switch but will consider the multi-match, single line condition. Note that you can use -cmatch for case- sensitive matching.

# Matches second occurrence of match when starting in position 7 on a line
Get-ChildItem 'C:\Temp\*.cbl' -Recurse -File | ForEach-Object {
    $text = Get-Content -LiteralPath $_.Fullname -Raw
    if ($text -match '(?sm)(\A.*?^.{6}PROCEDURE DIVISION\..*?^.{6})PROCEDURE DIVISION\.(.*)\Z') {
        Write-Host "Changing file $($_.FullName)"
        $matches.1+$matches.2 | Set-Content $_.FullName
    }
}
    
AdminOfThings
  • 23,946
  • 4
  • 17
  • 27
  • Hi! This works great! Thank you. It is much better than my hack from above. I do have a small snag... Is it possible to alter the search to only replace hits in position 7? – user3166462 Jan 11 '21 at 20:48
  • @user3166462 What does position 7 mean? The 7th character of a current line? Line number 7? Or is your file delimited somehow and you mean column 7? – AdminOfThings Jan 11 '21 at 20:52
  • It's position 7 of each line. 123456PROCEDURE DIVISION. OR XXXXXXPROCEDURE DIVISION. OR PROCEDURE DIVISION. – user3166462 Jan 11 '21 at 21:00
  • @user3166462 I added code to only change it when it occurs in position 7 and when it is the second match. – AdminOfThings Jan 11 '21 at 21:16
  • Hi, Thank you for the code change but that does not work. It is leaving the commented out string and removing the needed string. It is finding 2 occurrences, yes. The issue is one occurrence is not in position 7 and the other is. It is leaving the one not in position 7 and removing the one in position 7. It needs to match 2 strings IN position 7 and remove one. All other matches in any other position should be ignored. Is something like that possible, to actually match on position 7? Other than that your code is way better than my attempt! – user3166462 Jan 11 '21 at 21:50
  • I added a condition to only match the text if it starts at position 7. Then only remove the second occurrence. – AdminOfThings Jan 11 '21 at 21:59
  • That did it! Thank you! – user3166462 Jan 11 '21 at 22:09
  • AdminOfThings... Thank you for all the help. It solved the current issue. Another issue came up and I thought I could alter your code to fix it. Not having much luck. In another code drop repository, there is the same issue but when I use the above code to remove the string, there are still artifacts on the line that need to be removed also. Is there a way to alter thee code to remove the entire 2nd line instead of just the matching string? – user3166462 Jan 12 '21 at 12:14
  • One other thing if I may. If you could, would you break down the above code? I'm trying to understand how you got the results you did and I cannot figure it out for the life of me. – user3166462 Jan 12 '21 at 12:23