如何 - 仅查找和替换第一个匹配项

How to - Find and replace the first occurrence only

我有一个脚本,它似乎可以正常工作,但效果很好。 我的文件包含多行字符串“PROCEDURE DIVISION.”,句点在末尾。

我需要做什么...

仅删除字符串“PROCEDURE DIVISION”的 [2nd occurrence]。如果它在文本文件中出现两次,如果只找到一次则绕过该文件。我需要保留第一次出现和 change/remove 第二次出现。

我可以轻松找到并替换所有匹配项,但我不知道如何只替换 2 个中的 1 个。

这可以使用 Powershell 吗?

到目前为止,这是我的代码...

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

更新

我让它工作了,但它并不漂亮。

唯一的问题是捕获评论部分中的字符串。 我需要做的只是在每行中从位置 8 开始时将字符串计为命中。

这可能吗?

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
      } 

这可能有点乱七八糟,但它确实有效。 $myMatches = $pattern.Matches 在下面的例子中给出了 3 个匹配项,$myMatches[1].Index 是您要替换的字符串第二次出现的位置。

$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
}

试试这个:

$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

}

您可以为此使用 switch

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
    }
}

所有提供的答案都存在潜在问题。使用 switch 语句读取文件可能是最快的方法。但它需要考虑 PROCEDURE DIVISION. 在同一行中多次出现。下面的方法将比使用 switch 占用更多内存,但会考虑多匹配、单行条件。请注意,您可以使用 -cmatch 进行区分大小写的匹配。

# 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
    }
}