I have a couple of scripts and modules that use global variables for a number of things. My logging can take one of three forms; Terse, Verbose and Validation (Verbose logging with no actual actions, just validation of provided data). I also have a number of functions that respond differently depending on the Context they are run in (User or Machine), and the Action being executed (Rollout, Remove, Conform, Relocate) impacts things as well. So, thus far I have used three global variables and it has worked, but I know best practice is to avoid global variables. That said, the only way to address it seems to be some Get and Set functions in one module, and Script level variables. Which seems like it adds complexity without really addressing much, since I still have variables that are in a higher scope than the function using them. Alternatively I could pass those three values to every function that needs them, but that also is lots of arguments that add little value. So I start wondering, is this one place where global variables really are the correct answer? Their existence suggests there must be some situation where one should use them, and this is starting to feel like exactly that situation.
Using global variables causes very tight coupling of code. Using global variables causes namespace pollution. This may lead to unnecessarily reassigning a global value. Testing in programs using global variables can be a huge pain as it is difficult to decouple them when testing.
Use of the Global Variable in CThe global variables get defined outside any function- usually at the very top of a program. After this, the variables hold their actual values throughout the lifetime of that program, and one can access them inside any function that gets defined for that program.
Non-const global variables are evil because their value can be changed by any function. Using global variables reduces the modularity and flexibility of the program. It is suggested not to use global variables in the program.
You can access the global variables from anywhere in the program. However, you can only access the local variables from the function. Additionally, if you need to change a global variable from a function, you need to declare that the variable is global. You can do this using the "global" keyword.
There's nothing wrong with using global variables in specific scenarios, particularly for global settings or singleton objects that are defined once and then used throughout the rest of your code without further modification. Key point is to avoid changing the variables.
What you should not use global variables for is for transporting state, i.e. changing values. If you change the value of global variables at different places in your code troubleshooting problems becomes a huge pain in the rear, because you're passing information outside of the regular "channels" (parameters, return values). If you need to modify a global variable somewhere else in your code it's practically always a sign that you should re-evaluate your architecture.
PowerShell implements Dynamic Scoping. This is a rare choice in language design these days, but properly applied dynamic scoping gives a developer much better control over name collisions than global variables. To understand how this might apply to your case(s), lets consider the following toy module:
# File Module1.psm1
$module = "Module1"
$Context = "User"
function Log-Message( $message ) {
Write-Host "$module/${action}: $message ($LogLevel-$Context)"
}
function CommonCode {
Log-Message "In CommonCode"
}
function Invoke-Rollout( $LogLevel = "Terse", $Context=$script:Context) {
$action = "Rollout"
CommonCode
}
function Invoke-Remove( $LogLevel = "Terse", $Context=$script:Context) {
$action = "Remove"
CommonCode
}
function Set-Module1( $Context=$script:Context ) {
$script:Context = $Context
}
Export-ModuleMember -Function Invoke-Rollout, Invoke-Remove, Set-Module1
What's important here is that in Log-Message
, the variables $module
, $action
, $LogLevel
and $LogContext
are not global variables, instead they are free variables who's scope has not yet been determined. At runtime, PowerShell will dynamically determine their binding based on the most recent definition in the call stack...
Rather than trying to explain this in detail, it may be best for you to play around with this toy module and see what the effect of dynamic scoping is on the logging. Here are some experiments I tried:
PS C:\temp> Import-Module -Force .\Module1.psm1
PS C:\temp> Invoke-Rollout
Module1/Rollout: In CommonCode (Terse-User)
PS C:\temp> # For Sticky Change -- Set-Module1
PS C:\temp> Set-Module1 -Context Machine
PS C:\temp> Invoke-Rollout
Module1/Rollout: In CommonCode (Terse-Machine)
PS C:\temp> Invoke-Remove -LogLevel Verbose -Context XXX
Module1/Remove: In CommonCode (Verbose-XXX)
PS C:\temp> Invoke-Remove -Context User
Module1/Remove: In CommonCode (Terse-User)
PS C:\temp> $Context = "FooBar" # This should have no effet on Module1
PS C:\temp> Invoke-Remove
Module1/Remove: In CommonCode (Terse-Machine)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With