Почему оператор powershell -replace не правильно включает все символы, захваченные в группе захвата? - PullRequest
0 голосов
/ 08 ноября 2018

Edit:

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


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

Если вам нужно сослаться на другие переменные в выражении замены (как вы, возможно), вы можете использовать строку в двойных кавычках и экранировать доллары с обратным тиком

Однако я вижу интересное поведение, которое не могу объяснить.

$VersionReplacementRegex = "(\d+\.)\d+" #capture first digit + dot b/c I want to keep it
$BuildVersionValidationRegex = "\d+\.\d+\.\d+"

$VersionData = [regex]::matches("some-18.11.8",$BuildVersionValidationRegex)
$NewVersion = $VersionData[0] #matches 18.11.8

$filecontent = "stuff 1.0.0.0 other stuff" #Get-Content($file)

замена текста в $filecontent с использованием группы захвата, как указано в связанном вопросе, дает неполный результат ...

$filecontent -replace $VersionReplacementRegex, "`$1$NewVersion" | Write-Host

возвращает: 118.11.8 ожидается: 1.18.11.8

Но добавление пробела между $1 и $NewVersion дает другой, но одинаково бесполезный результат ..

$filecontent -replace $VersionReplacementRegex, "`$1 $NewVersion" | Write-Host

возвращает: 1. 18.11.8 Захваченная точка появляется здесь, но появляется и нежелательное пространство.

В этом примере результаты несколько схожи, но кажется, что группа захвата получает неправильное значение все вместе.

$NewVersion = 18.11.8
$filecontent = "stuff 5.0.0.0 other stuff"
$filecontent -replace "(\d+\.)\d+", "`$1$NewVersion" | Write-Host

# returns: 118.11.8
# expected: 5.18.11.8

Добавление пробела в строке замены возвращает: 5. 18.11.8

Итак, что мне не хватает, или есть лучший способ сделать это?

1 Ответ

0 голосов
/ 09 ноября 2018

Судя по прошлому опыту, PetSerAl , предоставивший ключевой указатель в комментарии к вопросу, не вернется, чтобы опубликовать ответ.

Т.Л., др

Если вы используете -replace с операндом-заменителем, который ссылается на переменные захвата и PowerShell , используйте синтаксис, такой как "`${<ndx>}${<PsVar>}", где <ndx> - это индекс вашей группы захвата, а <PsVar> - это имя вашей переменной PowerShell; обратите внимание на ` перед первым $:

PS> $var = '2'; 'foo' -replace '(f)', "[`${1}$var]"
[f2]oo # OK, -replace saw '${1}2'

Если вы пренебрегаете использованием {...} для устранения неоднозначности индекса группы захвата, сбои замены, потому что интерполированное строковое значение тогда эффективно ссылается на другой индекс:
-replace затем видит [$12], который из-за для ссылки на несуществующую группу захвата с индексом 12, остается как есть:

PS> $var = '2'; 'foo' -replace '(f)', "[`$1$var]"
[$12]oo # !! -replace saw '$12', i.e., a nonexistent group with index 12

Сложно смешать расширение строки PowerShell (интерполяция) с синтаксисом оператора
-replace
, потому что легко запутаться

  • В строках с двойными кавычками ("...") это общая функция раскрытия строк в PowerShell (интерполяция строк), которая сначала интерпретирует $ символов , где префикс $ относится к (PowerShell) переменным и внутри $(...), целым операторам .

  • Какой бы строкой не был результат этого расширения равен , тогда интерпретируется оператором -replace, где $ префиксные токены относятся к результатам операция сопоставления регулярных выражений , как указано в , этот ответ .

  • Обратите внимание, что эти слои интерпретации $ полностью не связаны, и тот факт, что оба используют сигил $, является случайным.

Таким образом:

  • Если ваш операнд-заменитель не не нуждается в расширении строки , т. Е. Если нет необходимости ссылаться на выражения PowerShell для переменных или , обязательно используйте строку в одинарных кавычках ('...') , чтобы расширение строки PowerShell не вступало в игру:

     PS> 'foo' -replace '(f)', '[$1]'
     [f]oo  # OK - if you had used "[$1]" instead, the output would be '[]oo',
            # because $1 is then interpreted as a *PowerShell variable*.
    
  • Если вам нужно необходимо задействовать расширение строки :

    • Префикс $ символов. , которые должны быть переданы в -replace с `

      • ` (обратный удар) является общим escape-символом PowerShell, а в "..." строках он используется для указания того, что следующий символ должен быть взят буквально ; помещенный перед $, он подавляет интерполяцию строк для этого токена; например, "I'm `$HOME" дает литерал I'm $HOME, то есть ссылка на переменную была , а не развернута.
    • К устраняет неоднозначность ссылок на группы захвата , например, $1, заключают их в {...} - например, ${1}

      • Обратите внимание, что вам также может понадобиться использовать {...} для устранения неоднозначности PowerShell имен переменных; например "$HOME1" должно быть "${HOME}1", чтобы успешно ссылаться на переменную $HOME.
      • Кроме того, речь идет не только о группе захвата индексы ; двусмысленность может также возникнуть с именованными группами захвата; в операндах на основе "...", всегда использующих {...} вокруг индексов / имен групп захвата (и переменных PS) - хорошая привычка для формирования.
    • Если сомневаетесь, выведите замещающий операнд самостоятельно, чтобы проверить, что в конечном итоге увидит -replace.

      • В приведенном выше примере сам вывод "[`$1$var]", который применяет шаг интерполяции строк, сделал бы проблему более очевидной: [$12]

Для иллюстрации последнего пункта:

PS> $var = '2'; 'foo' -replace '(f)', "[`$1$var]"
[$12]oo  # !! $1 wasn't recognizes as the 1st capture group.

Проблема заключалась в том, что -replace после раскрытия строки видел [$12] в качестве операнда замены, и поскольку не было группы захвата с индексом 12, он был оставлен как есть.

Включение номера группы захвата в {...} решает проблему:

PS> $var = '2'; 'foo' -replace '(f)', "[`${1}$var]"
[f2]oo  # OK
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...