Author Posts

January 1, 2012 at 12:00 am

by scottbass at 2013-03-07 21:09:49

Hi,

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

by DexterPOSH at 2013-03-08 22:23:51

Hi Scott,

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 that
PS>.\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 ?

by scottbass at 2013-03-09 07:19:22

Hi Dexter:

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...

by DexterPOSH at 2013-03-22 16:16:33

Have you tried tracing for what is happening behind the scenes here ?
I am not sure that will help but that is the only thing I can think of right now...that could be done

by MasterOfTheHat at 2013-03-25 09:29:14

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.