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.
    • ? { -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.

      • 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).
    • $false -ne ... then tests the pipeline's output for not being $false, which implies that Test-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.

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
}

Tags:

Powershell