To complement FoxDeploy's helpful answer with the wider perspective of pipeline use:
tl;dr:
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 } }