Использовать Powershell для замены подсекции результата регулярного выражения

Используя Powershell, я знаю, как искать файл для сложной строки с использованием регулярного выражения и заменять его некоторым фиксированным значением, как в следующем фрагменте:

Get-ChildItem  "*.txt" |
Foreach-Object {
    $c = ($_ | Get-Content)
    $c = $c -replace $regexA,'NewText'
    [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}

Теперь я пытаюсь выяснить, как заменить подраздел каждого соответствия регулярного выражения. Можно ли это сделать одним плавным шагом, как описано выше? Или вам нужно извлечь каждое совпадение большего регулярного выражения, выполнить поиск и заменить его, а затем каким-то образом вернуть этот результат в исходный текст?

Чтобы пояснить пример, предположим, что в следующем тестовом тексте я хочу найти только 14xx-нумерованные экземпляры типа "TEST = * 1404" в следующем тексте и заменить 14xx на 16xx?

A 2180 1830 12 0 3 3 TEST=C1404
A 900 1830 12 0 3 3 TEST=R1413
A 400 1830 12 0 3 3 TEST=R1411
A 1090 1970 12 0 3 3 TEST=U1400
A 1090 1970 12 0 3 3 TEST=CSA1400
A 1090 1970 12 0 3 3 TEST=CSA1414
A 1090 1970 12 0 3 3 TEST=CSA140
A 1090 1970 12 0 3 3 TEST=CSA14001
A 1090 1970 12 0 3 3 TEST=CSA17001

т.е. Я хочу, чтобы получившийся текст был следующим, где вы заметите, что должны измениться только первые 6 строк:

A 2180 1830 12 0 3 3 TEST=C1604
A 900 1830 12 0 3 3 TEST=R1613
A 400 1830 12 0 3 3 TEST=R1611
A 1090 1970 12 0 3 3 TEST=U1600
A 1090 1970 12 0 3 3 TEST=CSA1600
A 1090 1970 12 0 3 3 TEST=CSA1614 <- Second instance of '14' shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA140 <- Shorter numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA14001 <- Longer numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA17001

Следующее регулярное выражение, похоже, выполняет поиск более крупных строк, где мне нужно делать замены, но я не знаю, какую функциональность в Powershell (replace?) использовать, чтобы просто заменить подстроку результатов. Кроме того, не стесняйтесь предлагать лучшее регулярное выражение, если это поможет.

$regexA = "\bTEST=\b[A-Za-z]+14\d\d\r"

Мне бы не пришлось жестко закодировать исчерпывающий список вещей, которые могут находиться между "=" и цифрами, такими как "R", "C", "CSA" и т.д.

Я работал над чем-то в течение часа или около того, где я получаю все совпадения для регулярного выражения, поиск внутри них, чтобы заменить 14 на 16, а затем запустите замену на исходный текст старыми и новыми значениями, например. replace($myText,"TEST=CSA1400","TEST=CSA1600"), но это не очень хорошо закрывает особые случаи, и мне кажется, что я направляюсь вниз по кроличьей дыре.

Ответ 1

Вам нужно сгруппировать подвыражения, которые вы хотите сохранить (т.е. поместить их между круглыми скобками), а затем ссылаться на группы через переменные $1 и $2 в заменяющей строке. Попробуйте что-то вроде этого:

$regexA = '( TEST=[A-Za-z]+)14(\d\d)$'

Get-ChildItem  "*.txt" | % {
  $c = (Get-Content $_.FullName) -replace $regexA,'${1}16$2' -join "`r`n"
  [IO.File]::WriteAllText($_.FullName, $c)
}

Ответ 2

Попробуйте следующее:

Get-ChildItem  "*.txt" |
Foreach-Object {
  $c = $_ | Get-Content | Foreach {$_ -replace '(?<=TEST=\D+)14(?=\d{2}(\D+|$))','16'}
  $c | Out-File $_.FullName -Enc Ascii
}

Ответ 3

Вот пример использования делегата scriptblock (иногда называемого оценщиком):

$regex = [regex]'( TEST=\D+)14(\d{2})\s*$'
$evaluator = { '{0}16{1}' -f $args[0].Groups[1..2] }
filter set-number { $regex.Replace($_, $evaluator) }

foreach ($file in Get-ChildItem  "*.txt")
 {
   ($file | get-content) | set-number | Set-Content $file.FullName
 }

Он, возможно, более сложный, чем оператор -replace, но позволяет использовать операторы powershell для создания заменяющего текста, поэтому вы можете делать все, что можно добавить в блок script.