Странные методы построения строк в системе.Процесс обслуживания.ServiceController и набор содержимого для записи хэш-таблиц

#powershell

Вопрос:

Если я сделаю $myobjects = Get-Service это в PowerShell 5.1

  • Затем $myobjects | Set-Content test.txt выводится список полных имен классов.
  • В то Set-Content test.txt $myobjects время как выдает список названий услуг.
  • Set-Content test.txt $myobjects[0] получает полное имя класса.
  • Set-Content test.txt @($myobjects[0]) получает название службы.

Я знаю, что Set-Content использует .ToString() метод для представления сложных объектов в файле для записи. Некоторые классы имеют значимые .ToString() методы, в то время как другие классы просто возвращают свои полные имена классов. Справедливо.

Я также знаю, что конвейер позволяет Set-Content получать элементы по $myobjects одному, и каждый раз .ToString() вызывается метод каждого элемента. Тогда как во втором виде Set-Content получает массив в целом, и поэтому .ToString() вызывается метод массива.

Но: Разве .ToString() метод массива не сводится к .ToString() методам каждого отдельного элемента? Итак, в чем же разница? Согласно результату обеих команд, метод массива .ToString() кажется более «значимым», чем .ToString() метод каждого отдельного объекта типа [System.ServiceProcess.ServiceController] . Но как это может быть, если строка массива просто запрашивает методы объектов? .ToString()

Я думаю, что это может иметь какое-то отношение к тому факту, что .ToString() метод Системы.Процесс обслуживания.ServiceController определяется как система сценариев.Объект toString (), тогда как обычно .ToString() метод определяется как метод string toString(). Но где же точная причинно-следственная связь и какое это имеет отношение к массивам?

Один дополнительный вопрос: Нормально ли, что содержимое набора автоматически выдает обозначения хэш-таблицы, если вы пытаетесь записать объекты класса [PSCustomObject] в файл, даже если .ToString() метод PSCustomObject выдает пустую строку?

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

Ответ №1:

После некоторого расследования я предполагаю, что проблема, с которой вы столкнулись, заключается в ошибке во внутреннем преобразователе типов. Он не использует метод сценария, когда был предоставлен скалярный объект, но я пока не уверен.

Фон

PowerShell использует внутренний преобразователь типов, который предоставляется пользователю следующим способом:

 [System.Management.Automation.LanguagePrimitives]::ConvertTo()
 

Вы также можете просто привести что-то к [string] тому же эффекту или [string[]] получить тот же эффект, поскольку он используется ConvertTo() за кулисами, что отвечает на ваш вопрос о pscustomobject/hashtable.

 $newCustomObject = [PSCustomObject]@{
    Test = 'ABC'
}

[string]$newCustomObject

# returns:
# @{Test=ABC}
 

Переопределенная строка toString()

Как вы уже выяснили, метод ToString() System.ServiceProcess.ServiceController был переопределен системой расширенных типов PowerShell (ETS). Это просто вызывает $this.ServiceName

 $services[0].psobject.Methods.Match('toString') 

# returns:
# Script              : $this.ServiceName
# OverloadDefinitions : {System.Object ToString();}
# MemberType          : ScriptMethod
# TypeNameOfValue     : System.Object
# Value               : System.Object ToString();
# Name                : ToString
 

Из-за этого переопределенного метода $services[0].ToString() возвращает имя службы, тогда как $services[0].psbase.ToString() (метод базового/необработанного объекта) возвращает имя типа.

Воспроизведение

Я могу воспроизвести преобразование следующим образом:

 [System.Management.Automation.LanguagePrimitives]::ConvertTo($services[0] , [string])

# returns:
# System.ServiceProcess.ServiceController
 
 [System.Management.Automation.LanguagePrimitives]::ConvertTo([object[]]$services[0] , [string])

# returns:
# AarSvc_d165c2
 

След

Отслеживание двух приведенных выше преобразований возвращает следующее:

 Trace-Command -PSHost -Name TypeConversion -Expression { 
    [System.Management.Automation.LanguagePrimitives]::ConvertTo([object[]]$services[0] , [string])
}

# returns:
# DEBUG: 2021-05-17 18:49:38.6355 TypeConversion Information: 0 : Value to convert is scalar.
# DEBUG: 2021-05-17 18:49:38.6358 TypeConversion Information: 0 : Converting "System.ServiceProcess.ServiceController" to "System.Object".
# DEBUG: 2021-05-17 18:49:38.6360 TypeConversion Information: 0 :     Result type is assignable from value to convert's type
# DEBUG: 2021-05-17 18:49:38.6362 TypeConversion Information: 0 : Converting "System.Object[]" to "System.String".
# DEBUG: 2021-05-17 18:49:38.6365 TypeConversion Information: 0 :     Converting object to string.
 
 Trace-Command -PSHost -Name TypeConversion -Expression {
    [System.Management.Automation.LanguagePrimitives]::ConvertTo($services[0] , [string])
}

# returns
# DEBUG: 2021-05-17 18:48:52.3834 TypeConversion Information: 0 : Converting "System.ServiceProcess.ServiceController" to "System.String".
# DEBUG: 2021-05-17 18:48:52.3838 TypeConversion Information: 0 :     Converting object to string.
 

Что будет дальше

Мы должны изучить исходный код класса LanguagePrimitives, чтобы выяснить, что происходит за кулисами между скаляром и коллекцией: https://github.com/PowerShell/PowerShell/blob/658837323599ab1c7a81fe66fcd43f7420e4402b/src/System.Management.Автоматизация/движок/LanguagePrimitives.cs

Дополнительная информация

Некоторая информация о преобразователе типов ETS:

ETS использует два основных типа преобразователей типов при вызове языковых примитивов.Преобразование(Система.Объект, Система.Тип) метод. При вызове этого метода PowerShell пытается выполнить преобразование типов с помощью стандартных преобразователей языка PowerShell или пользовательского преобразователя. Если PowerShell не может выполнить преобразование, он создает исключение PSInvalidCastException.

Более подробную информацию о преобразовании типов ETS вы можете найти здесь:
https://docs.microsoft.com/en-us/powershell/scripting/developer/ets/typeconverters?view=powershell-7.1

Комментарии:

1. Ты ведешь нас к бездне PowerShell. И вы-знающий гид. Большое спасибо за ваше расследование! Я думаю, мне придется перечитать вашу разработку еще несколько раз, чтобы полностью понять. На данный момент мне должно быть достаточно того, что я, очевидно, был захвачен неизвестным жуком. Простите меня, что я не просматриваю исходный код ваших ссылок. Но если вы узнаете больше, я с нетерпением буду читать и сделаю все возможное, чтобы понять это.