The following test sample can be used to reproduce different outcome based on how you run it.
Prerequisites: VSCode with PowerShell extension.
Steps:
- Create a new
*.ps1
file on your desktop and paste code below into it. - Open file with
VSCode
, bring up terminal and run ex..\filename.ps1
$private:PSDefaultParameterValues = @{}
$private:PSDefaultParameterValues.Add("*:ErrorVariable", " ErrorBuffer")
function Test-Variable
{
[CmdletBinding()]
param ()
"Test-Variable private: '$($private:PSDefaultParameterValues | Out-String)'"
"Test-Variable local: '$($local:PSDefaultParameterValues | Out-String)'"
"Test-Variable script: '$($script:PSDefaultParameterValues | Out-String)'"
"Test-Variable global: '$($global:PSDefaultParameterValues | Out-String)'"
$ErrorBuffer = $PSCmdlet.GetVariableValue("ErrorBuffer")
"Count of errors is $($ErrorBuffer.Count)"
Write-Error -Message "sample error"
"Count of errors is $($ErrorBuffer.Count)"
$ErrorBuffer.Clear()
}
Test-Variable
Output is this:
> .\filename.ps1
Test-Variable private: ''
Test-Variable local: ''
Test-Variable script: ''
Test-Variable global: ''
Count of errors is 0
Write-Error: C:\Users\User\Desktop\filename.ps1:24:1
Line |
24 | Test-Variable
| ~~~~~~~~~~~~~
| sample error
Count of errors is 1
Next, instead of running manually in VSCode select from menu Run -> Run Without Debugging
And here is the new output:
C:\Users\User> . 'c:\Users\User\Desktop\filename.ps1'
Test-Variable private: ''
Test-Variable local: ''
Test-Variable script: '
Name Value
---- -----
*:ErrorVariable ErrorBuffer
'
Test-Variable global: '
Name Value
---- -----
*:ErrorVariable ErrorBuffer
'
Count of errors is 0
Test-Variable: C:\Users\User\Desktop\filename.ps1:24:1
Line |
24 | Test-Variable
| ~~~~~~~~~~~~~
| sample error
Count of errors is 2
Suddenly there are now 2 errors!
I understand this is due to PSDefaultParameterValues
not being respected, global and script scope variable is set even though I explicitly declare it as private.
You can also try Run -> Start Debugging
and the result is same.
I was pulling my hair for hours trying to figure out what's wrong with my code just to discover this.
The question is, what's the issue and how to resolve it?
Is my code wrong or is there some issue with either PS extension
or VSCode
?
My goal is to have this code produce 1 count of errors no matter how I run it.
If you wonder the reason why I use this method, this is how I implement automated logging in PS without having to specify -ErrorVariable
everywhere, the reason why PSDefaultParameterValues
is private is because this ensures that only a script being called is seeing it, module scope functions and nested functions will pick it up from the caller (CmdletBinding
must exist for this to work).
I don't see any other way around, this is just the smallest reproducible sample of the issue.
CodePudding user response:
The problem is that running your script via the PowerShell extension - whether with or without debugging:
runs the script with
.
, the dot-sourcing operator and ...... given that the calling scope is the global scope, your script's code effectively runs directly in the global scope.
Therefore, from your function's perspective (which script-internally runs in a child scope), the $script:
and the $global:
scopes are the same.
Workaround:
Create a helper script alongside your target script, say, debugHelper.ps1
, with the following content:
# Call filename.ps1 in a *child* scope.
& $PSScriptRoot/filename.ps1
Then use it as the entry point for running or debugging your code.