by scottbass at 2013-03-07 21:09:49
Hi,by DexterPOSH at 2013-03-08 22:23:51
Save this test script as foo.ps1:[CmdletBinding()]
param(
[Alias("Fullname")]
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[AllowNull()]
<#
[ValidateScript(
{
# Allow $null (via the pipeline), or Strings and System.IO.* objects
if ($_ -eq $null) {Write-Warning "There are no files to process."; $true}
$objectType=$.GetType().Fullname
switch -wildcard ($objectType) {
"System.IO.*" {$true}
"System.String" {$true}
default {throw "Invalid -Path: $objectType is invalid."; $false}
}
}
)]
#>
[Object[]]$Path
)
process
{
# Process the pipeline
If ($Path -eq $null) {Write-Warning "There are no files to process.";return $null}
Foreach ($file in $Path) {$file}
}
Test it. I found it best to test in a debugger session with breakpoints on if ($ -eq $null)… and If ($Path -eq $null)…
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts> $null | .\foo.ps1
WARNING: There are no files to process.
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts> (gci foo -ea "SilentlyContinue") | .\foo.ps1
Verify that gci on a non-existant file returns $null:
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts> (gci foo -ea "SilentlyContinue") -eq $null
True
Now remove the comment block from the ValidationScript and re-test:
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts> $null | .\foo.ps1
C:\Documents and Settings\sbass\My Documents\My Powershell Scripts\foo.ps1 : Cannot validate argument on parameter ‘Path’. The argument is null, empty, or an element of the a
rgument collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
At line:1 char:18
+ $null | .\foo.ps1 <<<<
+ CategoryInfo : InvalidData: ([foo.ps1], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,foo.ps1
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts> (gci foo -ea "SilentlyContinue") | .\foo.ps1
PS C:\Documents and Settings\sbass\My Documents\My Powershell Scripts>
This isn’t a huge deal; I can get rid of the ValidationScript and code the checks in the main body of the program. It’s just that I find the current behaviour weird and inconsistent, and I’ve invested time with the ValidationScript approach.
Regards,
Scott
Hi Scott,by scottbass at 2013-03-09 07:19:22
When I read the about topic, it shows me below:
AllowNull Validation Attribute
The AllowNull attribute allows the value of a mandatory parameter
to be null ($null).
And your script perfectly allows thatPS>.\foo.ps1 -path $null
WARNING: There are no files to process.
But when you are piping you are passing the object and not passing a value, I guess.
Does this makes sense ?
Hi Dexter:by DexterPOSH at 2013-03-22 16:16:33
Without a validation script: piping $null gets passed to $path, the process section executes, and I get the warning. Piping gci of a non-existent file does nothing. But the test shows that gci of a non-existent file -eq $null
With a validation script: piping $null, the validation script fails, but the validation script should allow $null and return $true. Piping gci has the same results as above.
When I have a breakpoint in the debugger, gci non-existent doesn’t trigger the break.
It seems inconsistent to me…
Have you tried tracing for what is happening behind the scenes here ?by MasterOfTheHat at 2013-03-25 09:29:14
I am not sure that will help but that is the only thing I can think of right now…that could be done
The problem is that you aren’t exiting the validation script after you issue the Write-Warning and write $true to the output. Because of the way you have the validate script written, it continues to try and process the rest of the validate even if the param is $null. Simple enough fix, though. Just add "return" to where you want the script to exit.if ($_ -eq $null) {
Write-Warning "There are no files to process."
return $true
}
$objectType=$_.GetType().Fullname
switch -wildcard ($objectType) {
"System.IO.*"
{
return $true
}
"System.String"
{
return $true
}
default
{
throw "Invalid -Path: $objectType is invalid."
}
}
Also notice that I added return statements to your switch block and took out the "$false" on the default case. Take a look at the "ValidateScript Validation Attribute" section in about_Functions_Advanced_Parameters. You’ll see that the validate script will generate an if the script returns false or if the script throws an exception.