Функция, которая принимает параметры с привязкой к положению в качестве аргументов, а также из конвейера

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

Function Test
{
[CmdletBinding(SupportsShouldProcess,DefaultParameterSetName='def')]
Param(
  [Parameter(Position=0,ParameterSetName='def')]
  [String]$Pos1,
  [Parameter(ValueFromPipeline,Position=1,ParameterSetName='pip')]
  [String]$InputObject,
  [Parameter(Position=1,ParameterSetName='def')]
  [Parameter(Position=0,ParameterSetName='pip')]
  [String]$State
)
    Process
    {
        Switch ($PScmdlet.ParameterSetName)
        {
            'def' {Write-Host "${State}: $Pos1"}
            'pip' {Write-Host "${State}: $InputObject"}
        }
    }
}

PS C:\> 'this' | Test 'error'

test : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:10
+ 'test' | test 'error'
+          ~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (test:String) [Test], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Test

Я могу заставить конвейерный вызов функции с привязкой к положению работать со следующим примером:

Function Test
{
Param(
  [Parameter(Position=1,ValueFromPipeline)]
  [String]$Msg,
  [Parameter(Position=0)]
  [String]$State
)

    Process
    {
        Write-Host "${State}: $Msg"
    }
}

PS C:\> 'this' | Test 'error'
error: this

Итак, мой вопрос: как я могу создать функцию, которая будет принимать позиционно-привязанные аргументы в командной строке (Test 'message' 'status') и из конвейера ('message' | Test 'status') без необходимости явного вызова имени параметра ('message' | Test -State 'status')?


person Maximilian Burszley    schedule 25.09.2017    source источник
comment
Удалите ParameterSetName из вашей трубы, Remove ParameterSetName='pip'   -  person ArcSet    schedule 25.09.2017
comment
@ArcSet Это приводит к тому, что аргумент позиции 0 дважды привязывается к $InputObject и $State, но он перестал выдавать мне ошибки.   -  person Maximilian Burszley    schedule 25.09.2017


Ответы (1)


Давайте заменим блок Process { Write-Host "${State}: $Msg" } следующим фрагментом кода в последнем скрипте, чтобы сделать вывод более информативным и получить из него информацию:

Process
{
    ###  Write-Host "${State}: $Msg"
    Write-Host "State=$State`t Msg=$Msg" -NoNewline
    Write-Host "`t`t`t`t$($MyInvocation.Line.trim())" -ForegroundColor Cyan
}

Затем проверьте output для (почти) всех возможных комбинаций аргументов:

PS D:\PShell> 
'this' | Test    -State 'error'
'this' | Test           'error'
Test -Msg 'this' -State 'error'
Test -Msg 'this'        'error'
Test      'this' -State 'error'
Test      'this'        'error'

State=error  Msg=this               'this' | Test    -State 'error'
State=error  Msg=this               'this' | Test           'error'
State=error  Msg=this               Test -Msg 'this' -State 'error'
State=error  Msg=this               Test -Msg 'this'        'error'
State=error  Msg=this               Test      'this' -State 'error'
State=this   Msg=error              Test      'this'        'error'

PS D:\PShell> 

Мы можем видеть разные выходные данные для Test 'this' 'error' (без конвейера и без явно названных параметров в строке). Следовательно, Test 'error' 'this' может дать "правильный" результат.

Другой подход: давайте изменим вывод скрипта следующим образом:

Function Test
{
Param(
  [Parameter(Position=1,ValueFromPipeline)]
  [String]$Msg,
  [Parameter(Position=0)]
  [String]$State
)
    Process
    {
        #Write-Host "${State}: $Msg"
        $auxLine = $MyInvocation.Line.split( ' ', 
                [System.StringSplitOptions]::RemoveEmptyEntries)
        if ( $auxLine[0] -eq $MyInvocation.InvocationName -and
                '-Msg'   -notin $auxLine -and
                '-State' -notin $auxLine ) 
        {
            Write-Host "State=$Msg`t Msg=$State" -NoNewline
            Write-Host "`tALTERED`t`t$($MyInvocation.Line.trim())" -ForegroundColor Yellow
        } else {
            Write-Host "State=$State`t Msg=$Msg" -NoNewline
            Write-Host "`t`t`t`t$($MyInvocation.Line.trim())" -ForegroundColor Cyan
        }
    }
}

Вывод:

PS D:\PShell> 
'this' | Test    -State 'error'
'this' | Test           'error'
Test -Msg 'this' -State 'error'
Test -Msg 'this'        'error'
Test      'this' -State 'error'
Test      'this'        'error'

State=error  Msg=this               'this' | Test    -State 'error'
State=error  Msg=this               'this' | Test           'error'
State=error  Msg=this               Test -Msg 'this' -State 'error'
State=error  Msg=this               Test -Msg 'this'        'error'
State=error  Msg=this               Test      'this' -State 'error'
State=error  Msg=this   ALTERED     Test      'this'        'error'

PS D:\PShell> 
person JosefZ    schedule 01.10.2017
comment
Просто чтобы понять это, вы в основном проверяете неконвейерный ввод на наличие неявных аргументов, передаваемых по позиции, и переворачиваете вывод на основе этого? Хорошее использование $MyInvocation. - person Maximilian Burszley; 02.10.2017
comment
Немного сложнее, чем я надеялся (наборы параметров оставляют желать лучшего), но это определенно работает. Спасибо - person Maximilian Burszley; 02.10.2017