Как обеспечить преобразование пользовательского типа параметра PowerShell

#powershell #parameters #type-conversion

#powershell #параметры #преобразование типов

Вопрос:

У меня есть сценарий, в котором одним из параметров моего командлета является кортеж из имени файла CSV и имени заголовка столбца. Я хотел бы правильно импортировать это как кортеж с типом [System.Tuple[System.IO.FileInfo,String]] . Полный код выглядит следующим образом:

 [Parameter(Mandatory=$false)]
[ValidateScript( {
        if (-Not ($_.item1 | Test-Path) ) {
            throw "File or folder does not exist"
        }
        if (-Not ($_.item1 | Test-Path -PathType Leaf) ) {
            throw "The Path argument must be a file. Folder paths are not allowed."
        }
        return $true
    })]
[System.Tuple[System.IO.FileInfo,String]]$SomePath
 

Однако, когда вы приводите такой аргумент, как -SomePath book1.csv,'Some Column Header' вы получаете:

 Invoke-MyCmdlet.ps1: Cannot process argument transformation on parameter 'SomePath'. Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Tuple`2[System.IO.FileInfo,System.String]".
 

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

Мой вопрос таков: есть ли способ сообщить PowerShell, как правильно выполнить преобразование типов? Если да, то каков синтаксис для этого? Задокументировано ли это более четко в другом месте, и я пропустил это?

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

1. Это звучит как плохая идея. Есть ли причина не использовать два отдельных параметра? Или, возможно, вместо этого принять словарь (path или fileinfo в качестве ключа, имя заголовка в качестве значения) в качестве аргумента?

2. Просто создайте свой кортеж при передаче значения параметра: -SomePath [system.tuple]::Create([io.fileinfo]'book.csv','some column header')

3. @MathiasR.Jessen удобство для пользователя и для меня. Эти параметры всегда будут связаны друг с другом. Я мог бы разделить их, но тогда у меня все еще есть сложность обеспечения того, чтобы, если они предоставляют один аргумент, они также предоставляли другой. Я, конечно, открыт для изменения кода, но на самом деле мне все еще интересно, как это сделать.

4. @AdminOfThings, к сожалению, не вариант — я пишу это для кого-то, у кого, как я предполагаю, очень мало технических навыков. Передача типов — это не то, что я хочу, чтобы пользователь должен был делать.

5. Вы ожидаете принять несколько таких пар? Или только один?

Ответ №1:

Вы могли бы реализовать и зарегистрировать свои собственные PSTypeConverter (здесь с использованием классов PowerShell).:

 class CustomTupleConverter : System.Management.Automation.PSTypeConverter
{
  [bool]
  CanConvertFrom([object]$value, [type]$targetType)
  {
    # We only convert TO [Tuple[FileInfo,string]] 
    if($targetType -ne [System.Tuple[System.IO.FileInfo,string]]){
      return $false
    }

    # ... and only from 2-item arrays consisting of @([string],[string]) or @([FileInfo],[string])
    if($value -is [array] -and $value.Length -eq 2){
      if(($value[0] -is [string] -or $value[0] -is [System.IO.FileInfo]) -and $value[1] -is [string]){
        return $true
      }
    }

    return $false
  }

  [object]
  ConvertFrom([object]$value, [type]$targetType, [IFormatProvider]$format, [bool]$ignoreCase)
  {

    # Resolve individual values in the input array
    $fileInfo = if($value[0] -is [System.IO.FileInfo]){
      $value[0]
    }
    else{
      Get-Item -Path $value[0]
    }

    if($fileInfo -isnot [System.IO.FileInfo]){
      throw "Path didn't resolve to a file."
    }
    $headerName = $value[1] -as [string]

    # Create corresponding tuple and return
    return [System.Tuple]::Create($fileInfo, $headerName)
  }

  [bool]
  CanConvertTo([object]$value, [type]$targetType){
    return $this.CanConvertFrom($value, $targetType)
  }

  [object]
  ConvertTo([object]$value, [type]$targetType, [IFormatProvider]$format, [bool]$ignoreCase){
    return $this.ConvertFrom($value, $targetType, $format, $ignoreCase)
  }
}
 

После определения нам нужно зарегистрировать его как возможный преобразователь типов для [System.Tuple[System.IO.FileInfo,string]] :

 $targetTypeName = [System.Tuple[System.IO.FileInfo,string]].FullName
$typeConverter = [CustomTupleConverter]
Update-TypeData -TypeName $targetTypeName -TypeConverter $typeConverter
 

Теперь мы можем привязать 2 строки к кортежу:

 function Test-TupleBinding
{
  param(
    [Parameter(Mandatory = $true)]
    [System.Tuple[System.IO.FileInfo,string]] $PathAndColumnName
  )

  $PathAndColumnName
}
 

Магия покрытия в действии во время привязки параметров:

 PS C:> Test-TupleBinding -PathAndColumnName windowssystem32notepad.exe,ColumnNameGoesHere

Item1                           Item2              Length
-----                           -----              ------
C:windowssystem32notepad.exe ColumnNameGoesHere      2