【问题标题】:How to - Find and replace the first occurrence only如何 - 仅查找和替换第一个匹配项
【发布时间】:2021-04-16 17:20:48
【问题描述】:

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

我需要做什么......

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

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

这可以使用 Powershell 吗?

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

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

更新

我得到了这个工作,它并不漂亮。

唯一的问题是捕获 cmets 部分中的字符串。 我需要做的只是当字符串从每行的第 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
      } 

【问题讨论】:

  • 你好安德鲁!这是一个很棒的帖子,但它并没有解决我的问题。在阅读了另一篇文章后,我现在意识到我应该非常具体。我需要删除第二次出现的字符串并留下第一个。那可能吗?问候,-罗恩

标签: powershell replace find


【解决方案1】:

所有提供的答案都存在潜在问题。使用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
    }
}
    

【讨论】:

  • 嗨!这很好用!谢谢你。它比我从上面的 hack 好得多。我确实有一个小障碍...是否可以将搜索更改为仅替换位置 7 中的命中?
  • @user3166462 位置 7 是什么意思?当前行的第 7 个字符? 7号线?还是您的文件以某种方式分隔,您的意思是第 7 列?
  • 每行第7位。 123456程序部。或 XXXXXXPROCEDURE 部门。或程序部。
  • @user3166462 我添加了代码,仅当它出现在位置 7 并且是第二次匹配时才更改它。
  • 您好,感谢您更改代码,但这不起作用。它留下注释掉的字符串并删除所需的字符串。它发现了 2 次,是的。问题是一个事件不在第 7 位,另一个是。它将不在位置 7 中的一个保留在位置 7 中并删除一个。它需要在位置 7 匹配 2 个字符串并删除一个。应忽略任何其他位置的所有其他匹配项。这样的事情是否可能,实际上匹配第 7 位?除此之外,您的代码比我的尝试要好得多!
【解决方案2】:

这可能有点骇人听闻,但它确实有效。 $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
}

【讨论】:

    【解决方案3】:

    试试这个:

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

    【讨论】:

      【解决方案4】:

      您可以为此使用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
          }
      }
      

      【讨论】:

      • 嘿西奥,这行得通!谢谢。不幸的是,我遇到了另一个障碍。我现在需要按位置 7 进行搜索和替换。我现在在 cmets 部分中找到了 String Procedure Division,它正在抛弃脚本的准确性。是否可以仅在每行的第 7 位搜索字符串?
      • @user3166462 它是否仍然必须是第二次出现,仅在找到字符串从位置 7 开始时才计数?在这种情况下,将 switch 命令正下方的正则表达式更改为 '^.{6}PROCEDURE DIVISION\.'
      猜你喜欢
      • 2014-06-18
      • 2020-12-09
      • 2022-12-02
      • 1970-01-01
      • 1970-01-01
      • 2013-06-15
      • 2011-10-07
      • 1970-01-01
      • 2018-12-18
      相关资源
      最近更新 更多