Powershell: загадочное поведение -подобного оператора - PullRequest
0 голосов
/ 04 марта 2019

У нас есть приложение, которое хранит базу данных сервера - список имен серверов и другую связанную информацию.Иногда нам нужно экспортировать информацию в формате XML, чтобы обработать ее скриптом Powershell.Имена серверов в файле XML могут быть в простом («ServerXX») или FQDN («ServerXX.abc.com») форматах.Сценарий ищет имя сервера, которое всегда в простом формате, и результаты поиска должны содержать все простые и полные имена серверов, которые соответствуют искомому имени.

Основной оператор поиска (слегка упрощенный) выглядит следующим образом:

$FoundServer = ($ServerList | Where {$_.Name -match $ServerName+"*"})

$ ServerList - это массив строк (имен серверов).Выглядит просто и работает как положено.Обычно.

Странно то, что иногда сценарий не может найти некоторые FQDN.Например, если полное доменное имя в файле «ServerXX.abc.com», а мы ищем «ServerXX», полное доменное имя не найдено.В то же время поиск других имен работает как положено.При отладке скрипта видно, что выражение внутри {} буквально "ServerXX.abc.com" -like "ServerXX*".Это ДОЛЖНО быть правдой.Но полученный результат поиска пуст.И еще более интересная вещь, если имя поиска указано как «ServerXX.», «ServerXX.a» или с другими буквами из полного доменного имени, сценарий находит его.Если в файле указано одно и то же имя сервера без имени домена (в простой форме), сценарий его найдет.

Хорошо, и еще более загадочно то, что у нас есть два экземпляра установленного приложения,один для производства, другой для тестирования.Тестовый содержит гораздо меньшую базу данных сервера.Если я добавлю «невидимое» имя сервера из экземпляра prod к тестовому экземпляру и экспортирую базу данных, сценарий найдет это имя без проблем.

Если заменить -like на -match, проблема исчезнет.Так что это не проблема генератора файлов XML (это еще один сценарий PS, который генерирует PSCustomObject и экспортирует его через Export-CliXml).Это также не проблема некоторых невидимых или не-ANSI символов в имени сервера.Я также проверил содержимое файла XML вручную.Он огромен (несколько десятков мегабайт) и сложен, поэтому его довольно сложно проанализировать, но я не обнаружил какой-либо видимой проблемы.Структура XML выглядит правильно.

Я не понимаю этого случайного поведения.Может ли это быть как-то связано с размером файла XML?Недостаточно памяти в PS или что-то в этом роде?Мы используем Powershell v4.

1 Ответ

0 голосов
/ 05 марта 2019

Обратите внимание, что этот ответ не решение, потому что (на момент написания этой статьи) недостаточно информации для диагностики вашей проблемы;однако ваше использование операторов -like и -match заслуживает некоторого изучения.


$_.Name -match $ServerName+"*" (более кратко: $_.Name -match "$ServerName*") равно , а не аналогично $_.Name -like "$ServerName*":

  • -match использует регулярные выражения (регулярные выражения), , которое (также) соответствует части ввода , если явно не сформулировано, чтобы соответствовать в начале (^) и / или конце ($) ввода.

  • -like использует подстановочные выражения , которые должны соответствовать вводу в целом .

Хотя регулярные выражения и символы подстановки связаны между собой, их синтаксис и возможности различны;регулярные выражения гораздо более мощные ;в данном случае (обратите внимание, что при сопоставлении по умолчанию регистр не учитывается):

  • ... -like 'ServerXX*' соответствует строке, которая начинается с ServerXX и сопровождаетсяна ноль или более произвольных символов (*).

    • Входы 'ServerXX', 'ServerXX.foo.bar' и 'ServerXXY' будут возвращать $true.
  • ... -match 'ServerXX*' соответствует строке, содержащей подстроку ServerX (просто one X!) в любом месте ввода , если следоватьна ноль или более (*) X символов, поскольку дублирующий символ * изменяет предыдущий символ / подвыражение.

    • При вводе 'ServerXX' и 'ServerXX.foo.bar' вернет $true, так же как и 'ServerX' и 'fooServerXX' - что нежелательно в этом случае.

Если ваши входные данные являются полными доменными именами, используйте одно из следующихВыражения, которые эквивалентны:

... -like 'ServerXX.*'

... -match '^ServerXX\.'

Если имя сервера предоставляется через переменную , например, $ServerName, используйте "...", расширяемая улing , в простейшем случае:

... -like "$ServerName.*"

... -match "^$ServerName\."

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

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

... -like ('{0}.*' -f [System.Management.Automation.WildcardPattern]::Escape($ServerName))


... -match ('^{0}\.' -f [regex]::Escape($ServerName))

Использование строки шаблона в одинарных кавычках с -f, оператором формата ({0}представляет 1-й операнд RHS), делает очевидным, какие части используются буквально, и какие части объединяются как экранированное значение переменной.

...