Is there a simple way to ensure that all values returned are True? In the example below, I have a collection of objects which represent file. I want to make sure that all the sources file exist before proceeding. I pass all the paths to Test-Path and the function returns True/False for each file.
> $filesToUpdate = @(
[PsCustomObject] @{ Source = (Join-Path $basePath "Abc.module"); }
[PsCustomObject] @{ Source = (Join-Path $basePath "Def.module"); }
)
> $filesToUpdate.Source | Test-Path
True
True
> # How can I check if all the returned values are true?
How do I check all the returned values are True?
To complement FoxDeploy's helpful answer with the wider perspective of pipeline use:
tl;dr:
(Test-Path $filesToUpdate.Source) -contains $false
:
Fast, but unsuitable for very large collections, because the input collection must fit into memory as a whole (which it does in the context of this question).
Test-Path
accepts an array as input and outputs a parallel array reflecting each input item's existence; -contains
tests membership of the RHS scalar in the LHS array.
$false -ne ($filesToUpdate.Source | Test-Path | ? { -not $_ } | Select-Object -First 1)
This approach is a must if the input collection is too large to fit into memory as a whole / memory use must be kept constant.
$filesToUpdate.Source
is by definition an in-memory collection, but the scenario discussed applies to commands that produce a large number of output objects one by one and send them to the pipeline a such.? { -not $_ }
(short for: Where-Object { -not $_ }
) filters the Booleans output by Test-Path
to only contain the $false
values, if any. Thus, the filter will only produce output if at least one $false
value is present.
Select-Object -First 1
optimizes processing [PSv3+] by exiting the pipeline once the first object is received (the first $false
value), if any, which means that the output is either a single $false
value, or no output at all.
$false
) to none (no items map to $false
).$false -ne ...
then tests the pipeline's output for not being $false
, which implies that Test-Path
returned $true
for all input paths.
$false
must be used, because -not (...)
/ ! (...)
would not work as intended, because negating a command that produces no output also yields $true
.Generally, pipelines are a powerful concept integral to PowerShell and, even though they introduce processing overhead, they are worth using for their conceptual elegance, unless they present a performance problem.
If performance is paramount, pipelines can be worked around, which can be cumbersome, however.
Conversely, PowerShell's flexible operators sometimes offer solutions that are both conceptually elegant and fast, as is the case here, though potentially at the expense of memory consumption.
The only reason to use a pipeline in such scenarios would be to deal with large input collections.
To give you a sense of relative performance, here are test timings comparing the solutions, parameterized by the size of the input collection and how many runs to average.
Find the test script at the bottom.
Note that the input data is constructed to place the first (and only) nonexistent path in the middle of the input collection.
This choice dramatically affects the performance of the Select-Object -First 1
solution:
if you instead place a nonexistent path at the beginning, it will perform best, if you place it at the end or do not include one at all, there will be no performance again (on the contrary).
Sample numbers from my machine (late-2012 iMac), in seconds:
> .\Test.ps1 -count 10 -repeat 10 # 10 input items, timing averaged over 10 runs
Command 10-run average
------- --------------
-contains, no pipeline .00124
-contains, pipeline .00170
pipeline, where-object, select -first 1 .00276
pipeline, where-object .00314
pipeline, where-object, Test-Path in script block .00460
> .\Test.ps1 -count 100 -repeat 10
Command 10-run average
------- --------------
-contains, no pipeline .01190
pipeline, where-object, select -first 1 .01328
-contains, pipeline .01836
pipeline, where-object .02365
pipeline, where-object, Test-Path in script block .03725
> .\Test.ps1 -count 1000 -repeat 10
Command 10-run average
------- --------------
pipeline, where-object, select -first 1 .11154
-contains, no pipeline .11764
-contains, pipeline .16508
pipeline, where-object .22246
pipeline, where-object, Test-Path in script block .37015
> .\Test.ps1 -count 10000 -repeat 10
Command 10-run average
------- --------------
pipeline, where-object, select -first 1 1.09919
-contains, no pipeline 1.15089
-contains, pipeline 1.75926
pipeline, where-object 2.21868
pipeline, where-object, Test-Path in script block 3.65946
Test.ps1
param(
[int] $count = 50
,
[int] $repeat = 10
)
# Create sample input array.
$paths = @('c:') * $count
$paths[$paths.Count / 2] = 'nosuch'
$timingPropName = "$repeat-run average"
@(
[pscustomobject] @{ Command = "-contains, no pipeline"; $timingPropName =
(1..$($repeat) | % { (Measure-Command { (Test-Path $paths) -contains $false }).TotalSeconds } |
Measure-Object -average | % Average) }
[pscustomobject] @{ Command = "-contains, pipeline"; $timingPropName =
(1..$($repeat) | % { (Measure-Command { ($paths | Test-Path) -contains $false }).TotalSeconds } |
Measure-Object -average | % Average) }
[pscustomobject] @{ Command = "pipeline, where-object, select -first 1"; $timingPropName =
( 1..$($repeat) | % { (Measure-Command { $paths | Test-Path | ? { $_ -eq $false } | Select-Object -First 1 }).TotalSeconds } |
Measure-Object -average | % Average) }
[pscustomobject] @{ Command = "pipeline, where-object"; $timingPropName =
(1..$($repeat) | % { (Measure-Command { $paths | Test-Path | ? { $_ -eq $false } }).TotalSeconds } |
Measure-Object -average | % Average) }
[pscustomobject] @{ Command = "pipeline, where-object, Test-Path in script block"; $timingPropName =
(1..$($repeat) | % { (Measure-Command { $paths | ? { !(Test-Path $_) } }).TotalSeconds } |
Measure-Object -average | % Average) }
) |
Sort-Object $timingPropName |
Format-Table Command, @{ n=$timingPropName; e={ '{0:.00000}' -f $_.$timingPropName } }
if (($filesToUpdate.Source | Test-Path) -contains $false){
#We know that one of the attempts was a failure
}
else{
#proceed
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With