Контекст
Рассмотрим следующую вспомогательную функцию:
Filter If-Null(
[Parameter(ValueFromPipeline=$true)]$value,
[Parameter(Position=0)]$default
) {
Write-Verbose "If ($value) {$value} Else {$default}"
if ($value) {$value} else {$default}
}
По сути, это оператор объединения с нулевым значением, реализованный как конвейерная функция. Он должен работать так:
PS> $myVar = $null
PS> $myVar | If-Null "myDefault" -Verbose
VERBOSE: If () {} Else {myDefault}
myDefault
Однако, когда я устанавливаю $myVar
в первый элемент в пустом массиве...
PS> $myVar = @() | Select-Object -First 1
... который фактически должен быть таким же, как $null
...
PS> $myVar -eq $null
True
PS> -not $myVar
True
...тогда трубопровод больше не работает:
PS> $myVar | If-Null "myDefault" -Verbose
Выхода нет вообще. Нет даже многословной печати. Это означает, что If-Null
даже не выполняется.
Вопрос
Получается, что @() | select -f 1
, хотя и относится к -eq
-$null
, является несколько другим $null
, который каким-то образом ломает трубопровод?
Кто-нибудь может объяснить такое поведение? Что мне не хватает?
Дополнительная информация
PS> (@() | select -f 1).GetType() You cannot call a method on a null-valued expression. At line:1 char:1 + (@() | select -f 1).GetType() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS> (@() | select -f 1) | Get-Member Get-Member : You must specify an object for the Get-Member cmdlet. At line:1 char:23 + (@() | select -f 1) | Get-Member + ~~~~~~~~~~ + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
PS> $PSVersionTable Name Value ---- ----- PSVersion 5.0.10586.117 PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.10586.117 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
Решение
Объяснение Ансгара верно (лучшее объяснение можно найти в ответ mklement0 на повторяющийся вопрос). Я просто хотел поделиться своим решением проблемы.
Я исправил If-Null
таким образом, что он возвращает $default
, даже если ничего не обрабатывается:
Function If-Null(
[Parameter(ValueFromPipeline = $true)]$value,
[Parameter(Position = 0)]$default
) {
Process {
$processedSomething = $true
If ($value) { $value } Else { $default }
}
# This makes sure the $default is returned even when the input was an empty array or of
# type [System.Management.Automation.Internal.AutomationNull]::Value (which prevents
# execution of the Process block).
End { If (-not $processedSomething) { $default }}
}
Эта версия теперь правильно обрабатывает результаты пустого конвейера:
PS> @() | select -f 1 | If-Null myDefault
myDefault
@()
- это массив нулевой длины, поэтому вы наблюдаете такое поведение.$a = @(); $a.GetType()
. Я имею в виду, что вы передаете объект, и ваш фильтр правильно возвращает этот объект, который представляет собой массив нулевой длины. - person 4c74356b41   schedule 10.01.2017@() | select -f 1
не является массивом нулевого размера. PowerShell говорит мне, что это от-eq
ual до$null
. - person Good Night Nerd Pride   schedule 10.01.2017