К сожалению, есть нет Собственный способ PowerShell, начиная с Windows PowerShell v5.1 / PowerShell Core v6.1 , и хотя [Linq.Enumerable]::Any()
является как минимум кратким, он может сломать в различных сценариях:
# These invocations all FAIL with
# 'Cannot find an overload for "Any" and the argument count: "1"'
foreach ($enumerable in 1.5, $null, (& {}), (1,2,3)) {[Linq.Enumerable]::Any($enumerable)}
Теперь, 1.5
, $null
и & {}
(что соответствует"нулевая коллекция") делать не реализовывать [IEnumerable]
([System.Collections.IEnumerable]
или универсальный аналог), но в мире PowerShell все перечислимо, даже скаляры а в конвейере - но не с foreach
(!) - даже $null
.Заметным исключением является «нулевая коллекция» ([System.Management.Automation.Internal.AutomationNull]::Value
), единственная цель которой - сигнализировать, что перечислять ничего нельзя (и в качестве такового можно утверждать, что в качестве особого случая перечисления это следует реализовать [IEnumerable]
).
Однако 1, 2, 3
делает реализацию [IEnumerable]
: это обычный массив PowerShell типа [object[]]
;в то время как вы можете исправить этот конкретный случай с любопытным явным приведением [object[]]
, оно, очевидно, не является общим решением, так как потенциальное преобразование в массив вызывает полное перечисление - дополнительную информацию см. в нижнем разделе.
Надежное, но громоздкое решение PowerShell, предназначенное только для собственных функций, которое обрабатывает все вышеперечисленные случаи, будет (PSv4 +):
# Returns $True if $enumerable results in enumeration of at least 1 element.
# Note that $null is NOT considered enumerable in this case, unlike
# when you use `$null | ...`
$haveAny = try { $enumerable.ForEach({ Throw }); $False } catch { $True }
Вышеэто:
- неуклюжий и трудно запоминающийся.
- требует, чтобы входные данные уже были сохранены в переменной (
$enumerable
в приведенном выше примере).
Однако в контексте отложенной оценки PowerShell обычно происходит от прямых вызовов командлетов , отправляющих свой выходной объект по объекту через конвейер (другими словами, это PowerShell конвейер , который выполняет отложенную оценку), и этот аспект потерян при захвате вывода в переменную , потому что ПауэЗатем rshell статически собирает выходные данные в массив ([object[]]
).
Следовательно, собственное решение PowerShell будет гипотетический Test-Any
командлет , который:
- получает входные данные через конвейер , объект за объектом.
- выходы
$True
если получен хотя бы один входной объект.
Начиная с Windows PowerShell v5.1 / PowerShell Core v6.1, однако, остановить конвейер по требованию * невозможно1106 *, что необходимо сделать командлету Test-Any
для эффективной работы (во избежание полного перечисления):
Можно найти давний запрос функции на GitHub .
Реализация эффективная Test-Any
, которая работает вокруг ограничения путем использования частного PowerShell типа банкаможно найти в этом ответе моего, любезно предоставлено PetSerAl ;помимо использования частного типа, вы также понесли штраф за производительность компиляции по требованию при первом использовании в сеансе.
Необязательночтение: использование [Linq.Enumerable]::Any($enumerable)
с массивами PowerShell
1, 2, 3
(часто обозначаемое как @(1, 2, 3)
, но это не обязательно) является экземпляром массива [object[]]
, тип массива PowerShell по умолчанию,
Мне неясно, почему вы не можете передать такие массивы в .Any()
как есть , учитывая, что он работает с явным приведением к того же типа : [Linq.Enumerable]::Any([object[]] (1,2,3)) # OK
Сбор команды для мультиобъектного вывода также (неявно) создает массив [object[]]
, который, однако, может быть передан как-is: [Linq.Enumerable]::Any((Get-ChildItem)) # OK
Наконец, массив, созданный с специфическим типом , может быть передан как есть:
$intArr = [int[]] (1, 2, 3); [Linq.Enumerable]::Any($intArr) # OK
Если кто-нибудь знаетпочему вы не можете использовать непосредственно построенный массив [object[]]
в этом контексте и если для этого есть веские причины, сообщите нам.