How do I create a function that accepts multiple argument types from pipeline and command line?
Since your parameter type is string
it's coercing the file system info object into a string when you are not using the pipeline { Test-VEnv $_ }
. If you call the ToString()
method of either a System.IO.FileInfo
or System.IO.DirectoryInfo
object you'll see this. When you use the pipeline it binds the fullname alias giving you the full path.
You can see what PowerShell is doing to bind the input object using Trace-Command
. Here is an example of how to use it:
trace-command -name parameterbinding -expression {(dir C:\)[0] | ? {Test-VEnv $_}} -pshost
Here is the important part of the output:
BIND arg [PerfLogs] to parameter [Path] Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute] result returned from DATA GENERATION: System.String[] COERCE arg to [System.String[]] Parameter and arg types the same, no coercion is needed. BIND arg [System.String[]] to param [Path] SUCCESSFUL
Test-Path
does the same thing. Take a look at these three examples:
PS C:\Users\Andy> Test-Path (dir C:\)[0]FalsePS C:\Users\Andy> (dir C:\)[0] | Test-PathTruePS C:\> Test-Path (dir C:\)[0]True
Since my PWD is not
C:\
I get FALSE because the DirectoryInfo object is converted to string (ToString()
) which only gives the folder name. This is because the pipeline wasn't used.Since the pipeline is used it works because it is binding to PsPath with this parameter:
[Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('PSPath')][string[]]${LiteralPath},
Since the directory contains the folder the folder's name exists.
You might try the alias PsPath
for your binding. This is what Test-Path
uses:
param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$true)] [Alias('PsPath')] [String[]] $Path)process { foreach ($P in $Path) { Get-Item $p }}
Some tests:
Set-Location C:\Write-Host 1 Test-VEnv '.\Windows', '.\Program Files'Write-Host 2 Test-VEnv (dir)Write-Host 3 'Windows', 'Program Files' | Test-VEnvWrite-Host 4 dir | Test-VEnv
Output:
1 Directory: C:\Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 3/14/2012 3:41 AM Windows d-r-- 3/24/2012 7:46 PM Program Files 2d---- 2/18/2012 4:32 AM PerfLogs d-r-- 3/24/2012 7:46 PM Program Files d-r-- 3/25/2012 4:49 PM Program Files (x86) d---- 3/9/2012 9:57 PM Python27 d-r-- 3/4/2012 8:11 PM Users d---- 3/14/2012 3:41 AM Windows -a--- 3/4/2012 8:45 PM 1024 .rnd 3d---- 3/14/2012 3:41 AM Windows d-r-- 3/24/2012 7:46 PM Program Files 4d---- 2/18/2012 4:32 AM PerfLogs d-r-- 3/24/2012 7:46 PM Program Files d-r-- 3/25/2012 4:49 PM Program Files (x86) d---- 3/9/2012 9:57 PM Python27 d-r-- 3/4/2012 8:11 PM Users d---- 3/14/2012 3:41 AM Windows -a--- 3/4/2012 8:45 PM 1024 .rnd
@Andy gives some great information specifically addressing points in your question. My answer here is more of a supplement considering the broader implications. It probably only deserves to be a comment but the length and my included image prevent me from posting this as just a comment...
I recently examined the question of pipeline vs. direct input in Powershell with a specific goal towards making these input streams symmetric with respect to all classes of inputs and with respect to what defaults are applied. There are, by my reckoning, six equivalence classes of input to consider:
- no input
- null
- empty
- scalar
- list of normal values
- list of mixed values (i.e. some null or empty)
What one would typically expect when each of these inputs is sent to a function would be this corresponding list:
- default value
- null
- empty
- scalar
- list of normal values
- list of mixed values (i.e. some null or empty)
That is, with no input supplied the default value is used; otherwise the given value is used. This sounds almost trivial, practically a tautology, but there are some subtleties. Consider, for example, what does it mean to supply no input via the pipeline? Is it null or an empty collection? I contend the latter for, among other reasons, it allows the symmetry between streams I mentioned above. Furthermore, how you write both your function signature and your function body makes sometimes surprising impacts on some or all of these input classes with one or the other input stream. Thus, I further contend that there is a lot more to this "trivial" consideration than meets the eye at first glance. So much so that I wrote extensively about it in the articleDown the Rabbit Hole- A Study in PowerShell Pipelines, Functions, and Parameters,published on Simple-Talk.com. Included with the article is a wallchart that shows a table of the six equivalence input classes and what you get for each with different function templates. Here is a thumbnail of the wallchart:
Does it work if you change the type of $path from String[] to [System.IO.DirectoryInfo[]]?