如何 - 仅查找和替换第一个匹配项
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
}
}
我有一个脚本,它似乎可以正常工作,但效果很好。 我的文件包含多行字符串“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
}
}