Есть ли способ заставить Powershell использовать определенный формат для вывода функции?

Я хочу предложить (возможно, применить, но я еще не уверен в семантике) конкретный формат для вывода функции PowerShell.

about_Format. ps1xml (версия для PowerShell 7.1) говорит следующее: «Начиная с PowerShell 6, представления по умолчанию определены в исходном коде PowerShell. Файлы Format.ps1xml из PowerShell 5.1 и более ранних версий отсутствуют в PowerShell 6 и более поздних версиях. '. Затем в статье объясняется, как файлы Format.ps1xml можно использовать для изменения отображения объектов и т.д. / сильный>

Это вызывает несколько вопросов:

  1. Хотя они «не существуют», можно ли создавать / использовать файлы Format.ps1xml в версиях PowerShell выше 5.1?
  2. Могут они или нет, есть ли лучшая практика предложить PowerShell, как определенная функция должна форматировать возвращаемые данные? Обратите внимание на то, что "подсказка" является неотъемлемой частью того, что конвейерный характер вывода PowerShell должен быть сохранен: пользователь по-прежнему должен иметь возможность направлять вывод функции в Format-List или ForEach-Object и т. Д.

Например, командлет Get-ADUser возвращает объекты, отформатированные с помощью Format-List. Если я напишу функцию с именем Search-ADUser, которая вызывает Get-ADUser внутри и возвращает некоторые из этих объектов, вывод также будет отформатирован как список. Передача вывода в Format-Table перед его возвратом не удовлетворяет моим требованиям, потому что тогда вывод не будет обрабатываться как отдельные объекты в конвейере.

Пример кода:

function Search-ADUser {
  param (
    $Name,
    [ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
  )
  return Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties
}

Лучшие ответы должны касаться обоих вопросов, хотя второй более существенен.

Неприемлемые ответы включают предложения о том, что функция не должна принудительно применять формат и / или что пользователь должен направлять вывод функции в выбранное средство форматирования. Это очень субъективная позиция, и вопрос о том, придерживается ее большинство или нет, не имеет значения.

Я искал force function format #powershell-7.0 перед публикацией, но ни один из результатов поиска не оказался релевантным.


person SuperElitist    schedule 15.06.2021    source источник
comment
С помощью очень быстрого теста я смог создать новое определение формата для объектов «процесса» и импортировать его в PS v7.1.3 с помощью Update-FormatData, так что кажется, что эти файлы все еще поддерживаются.   -  person boxdog    schedule 15.06.2021


Ответы (1)


Хотя они «не существуют», могут ли Format.ps1xml файлы создаваться / использоваться в версиях PowerShell выше 5.1?

  • Да; фактически любой сторонний код должен использовать их для определения настраиваемого форматирования.

    • That *.ps1xml files are invariably needed for such definitions is unfortunate; GitHub issue #7845 asks for an in-memory, API-based alternative (which for type data already exists, via the Update-TypeData cmdlet).
  • Только данные форматирования, поставляемые с PowerShell, теперь жестко запрограммированы в исполняемый файл PowerShell (Core), предположительно из соображений производительности.

есть ли лучшая практика предложить PowerShell, как определенная функция должна форматировать возвращаемые данные?

Отсутствие способа определения данных форматирования на основе API требует следующего подхода:

  • Определите полное имя типа (ов) .NET, к которому должно применяться форматирование.

    • Если форматирование должно применяться к [pscustomobject] экземплярам, ​​вам необходимо (а) выбрать уникальное (виртуальное) имя типа и (б) назначить его экземплярам [pscustomobject] с помощью ETS (система расширенных типов); например.:

      • Для экземпляров [pscustomobject], созданных с помощью командлета Select-Object:

        # Assign virtual type name "MyVirtualType" to the objects output
        # by Select-Object
        Get-ChildItem *.txt | Select-Object Name, Length | ForEach-Object {
          $_.pstypenames.Insert(0, 'MyVirtualType'); $_
        }
        
      • Для [pscustomobject] литералов укажите имя типа через запись PSTypeName:

        [pscustomobject] @{
          PSTypeName = 'MyVirtualType'
          foo = 1
          bar = 2
        }
        
  • Создайте *.ps1mxl файл для этого типа и загружайте его в каждую сессию.

    • Если команды, использующие эти данные форматирования, определены в модуле, вы можете включить файл в свой модуль, чтобы он автоматически выполнялся при импорте модуля.

    • Для получения справки по созданию таких файлов см .:

Предложение GitHub № 10463 требует значительно упрощенного взаимодействия в рамках поддержки расширенного [OutputType()] атрибуты, указывающие желаемое форматирование.


Применено к вашей пробной функции:

  • Следующая функция создает (временный) файл *.ps1xml для своего типа вывода по запросу при первом вызове в сеансе, чтобы гарантировать применение (неявного) Format-Table форматирования для всех 5 свойств ( по умолчанию 5 или более свойств приводят к (неявному) Format-List форматированию).

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

    • Лучшим, но более сложным решением было бы упаковать вашу функцию в module, в папку которого вы можете поместить файл *.ps1mxl (например, SearchAdUserResult.Format.ps1xml), а затем указать PowerShell загрузить файл при импорте модуля с помощью клавиши FormatsToProcess в манифест модуля (*.psd1) - например, FormatsToProcess = 'SearchAdUserResult.Format.ps1xml'

  • Обратите внимание, что вы также можете создать файл *.ps1mxl непосредственно для экземпляров Microsoft.ActiveDirectory.Management.ADUser, которые Get-ADUser, но в этом случае форматирование будет применено к любой команде, которая генерирует такие объекты.

function Search-ADUser {
  param (
    $Name,
    [ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
  )

  # The self-chosen ETS type name.
  $etsTypeName = 'SearchAdUserResult'

  # Create the formatting data on demand.
  if (-not (Get-FormatData -ErrorAction Ignore $etsTypeName)) {

    # Create a temporary file with formatting definitions to pass to 
    # Update-FormatData below.
    $tempFile = Join-Path ([IO.Path]::GetTempPath()) "$etsTypeName.Format.ps1xml"

    # Define a table view with all 5 properties.
    @"
<Configuration>
<ViewDefinitions>
    <View>
      <Name>$etsTypeName</Name>
      <ViewSelectedBy>
        <TypeName>$etsTypeName</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Enabled</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>SamAccountName</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Name</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>emailAddress</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>proxyAddresses</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>
"@ > $tempFile

    # Load the formatting data into the current session.
    Update-FormatData -AppendPath $tempFile

    # Clean up.
    Remove-Item $tempFile
  }

  # Call Get-ADUser and assign the self-chosen ETS type name to the the output.
  # Note: To test this with a custom-object literal, use the following instead of the Get-ADUser call:
  #      [pscustomobject] @{ Enabled = $true; SamAccountName = 'jdoe'; Name = 'Jane Doe'; emailAddress = '[email protected]'; proxyAddresses = '[email protected]' }
  Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties | ForEach-Object {
     $_.pstypenames.Insert(0, $etsTypeName); $_
  }

}

Затем вы увидите желаемый табличный вывод на основе данных формата; например.:

Enabled SamAccountName Name     emailAddress     proxyAddresses
------- -------------- ----     ------------     --------------
True    jdoe           Jane Doe [email protected] [email protected]
person mklement0    schedule 15.06.2021