#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. И вы-знающий гид. Большое спасибо за ваше расследование! Я думаю, мне придется перечитать вашу разработку еще несколько раз, чтобы полностью понять. На данный момент мне должно быть достаточно того, что я, очевидно, был захвачен неизвестным жуком. Простите меня, что я не просматриваю исходный код ваших ссылок. Но если вы узнаете больше, я с нетерпением буду читать и сделаю все возможное, чтобы понять это.