The
$global:? variable in PowerShell is not a standard automatic variable but rather a global scope modifier applied to the built-in automatic variable
$?. The
$? variable stores the execution status of the last command, indicating success or failure. While
$? is inherently available in all scopes, prefixing it with
$global: explicitly accesses it from the global scope, which is redundant since it is already globally accessible by default.
The Nature of Automatic Variables
PowerShell distinguishes between variables that you create and those that the engine maintains automatically. Automatic variables are special because they store information about the state of the shell or the results of previous operations. These variables are read-only in most contexts, meaning you cannot assign new values to them directly. Common examples include
$PWD for the current working directory,
$HOME for the user profile path, and
$PSVersionTable for version details.
Understanding these variables is crucial for writing robust scripts. For instance, checking
$? immediately after a command allows developers to implement conditional logic based on whether that command succeeded. If you attempt to modify an automatic variable, PowerShell will typically throw an error or ignore the assignment, preserving the integrity of the shell's internal state. This behavior ensures that critical metadata remains consistent across different parts of a script or interactive session.
Decoding the $global: Prefix
In PowerShell, scope determines where a variable is visible and how long it persists. There are several scopes: global, script, local, private, and one for each function or loop. The
$global: prefix is used to access a variable from the global scope, regardless of where the code is currently executing. This is particularly useful when debugging nested functions or modules where a local variable might shadow a global one.
When you write
$global:?, you are explicitly telling PowerShell to look up the value of
? in the global namespace. However, the automatic variable
$? is already defined at the global level. Therefore, using
$global:? does not change the value or behavior of the variable; it simply makes the scope reference explicit. In many cases, this extra verbosity is unnecessary unless you are specifically dealing with scoping conflicts, which are rare for automatic variables like
$?.
How $? Works Internally
The
$? variable is a boolean indicator that reflects the success or failure of the last executed statement. It is set to
$true if the command completed without errors and
$false if it encountered an error. This includes cmdlets, functions, and even .NET method calls. It is important to note that
$? only tracks pipeline errors, not terminating errors that stop execution entirely.
For example, if you run a cmdlet that fails due to invalid parameters,
$? will be
$false. If the cmdlet runs successfully but returns no output,
$? remains
$true. This distinction is vital for error handling. Many developers use
$? in conjunction with
$LASTEXITCODE to check the exit code of external programs, providing a comprehensive view of the command's outcome.
Distinguishing $? from $LASTEXITCODE
While
$? indicates whether the PowerShell pipeline failed,
$LASTEXITCODE stores the return value of native Windows executables. A native program might return a zero (success) while PowerShell considers the operation successful, but if the native program returns a non-zero value,
$LASTEXITCODE captures that specific number. Understanding both variables allows for precise control flow in complex automation tasks.
Best Practices for Usage
Using
$? effectively requires careful placement. Since it is overwritten by every subsequent command, you must check its value immediately after the command you want to evaluate. Storing it in a temporary variable is a common pattern to preserve the result for later use. For example:
$result = Get-ChildItem -Path "C:\NonExistent" -ErrorAction SilentlyContinue
$status = $?
if ($status -eq $false) {
Write-Host "Command failed"
}
This approach ensures that the value of
$? is not lost if you need to perform additional checks or logging before acting on the result. Avoid relying on
$? inside loops without capturing the value, as intermediate commands within the loop can reset it unexpectedly.
Scope Considerations in Modules
When working with modules, variables are often scoped locally to the module. If you need to share the result of a command across module boundaries, understanding scope becomes critical. While
$? is global, other variables might not be. Using
$global: can help bridge these gaps, but it should be done sparingly to avoid polluting the global namespace with unnecessary variables.
Related articles such as
PowerShell: Trying to Write PS1 File in One Window with Two Tabs Pointing to Different Network Folders highlight the importance of context and scope in multi-window environments. Similarly,
Why Powershell Code Works on One Computer but Not Another? emphasizes how environment differences can affect variable availability and behavior, reinforcing the need to understand where variables originate.
Frequently Asked Questions
Can I assign a value to $? manually?
No, you cannot assign a value to
$? directly because it is a read-only automatic variable. Attempting to do so will result in an error. Instead, use try-catch blocks or check the exit code to manage flow.
Does $? reset after every command?
Yes,
$? is updated after every single command execution. If you run multiple commands in succession,
$? will reflect the status of the very last one executed, not the first.
Is $global:? different from $? in terms of value?
No, they hold the same value. The
$global: prefix merely specifies the scope, which is already global for
$?. Using either syntax yields identical results.
What happens if $? is false in a pipeline?
If
$? is false, it means the last command in the pipeline generated an error. Subsequent commands in the pipeline may still run depending on the error action settings, but
$? will remain false until another command succeeds.
Should I use $? or $LASTEXITCODE for batch files?
Use
$LASTEXITCODE for batch files and native executables, as
$? only reflects PowerShell's internal pipeline status. Native programs do not update
$? reliably, making
$LASTEXITCODE the correct choice for external tools.