How do I check if 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.- This solution is conceptually simplest and fastest (compared to unoptimized pipeline use).
$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.
- Note: Here, pipeline input
$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.
- Note: Here, pipeline input
? { -not $_ }
(short for:Where-Object { -not $_ }
) filters the Booleans output byTest-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.- The performance impact of this optimization depends entirely on the input data and can range from dramatic (the first item in a large input collection maps to
$false
) to none (no items map to$false
).
- The performance impact of this optimization depends entirely on the input data and can range from dramatic (the first item in a large input collection maps to
$false -ne ...
then tests the pipeline's output for not being$false
, which implies thatTest-Path
returned$true
for all input paths.- Note that explicit comparison with
$false
must be used, because-not (...)
/! (...)
would not work as intended, because negating a command that produces no output also yields$true
.
- Note that explicit comparison with
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
}