Context:
We have several pieces of our infrastructure that are managed by large sets of Bash scripts that interact with each other via the source command, including (and frequently chaining includes) of files that are created compliant with a standard Bash template we use. I know that this is a situation that should probably never have been allowed, but it’s what we have.
The template basically looks like this:
set_params() {
#for parameters in a file that need to be accessed by other methods
#in that file and have the same value from initialization of that
#file to its conclusion:
global_param1=value
#left blank for variables that are going to be used by other methods
#in the file, but don't have a static value assigned immediately:
global_param2=
}
main_internals() {
#user-created code goes here.
}
main() {
set_params
#generic setup stuff/traps go here
main_internals arg arg arg
#generic teardown stuff goes here
}
Using this structure, we have files include other files via the source command and then call the included files main methods, which wraps and modularizes most operations well enough.
Problem:
Some of the thorniest problems with this infrastructure arise when a new module is added to the codebase that uses a global variable name that is used somewhere else, unrelatedly, in the same sourced chain/set of files. I.e if file1.sh has a variable called myfile which is uses for certain things, then sources file2.sh, and then does some more stuff with myfile, and the person writing file2.sh doesn’t know that (in many cases they can’t be expected to–there are a lot of files chained together), they might put a non-local variable called myfile in file2.sh, changing the value in the variable with the same name in file1.sh
Question:
Assuming that global variable name conflicts will arise, and that localing everything can’t completely solve the problem, is there some way to programmatically unset all variables that have been set in the global scope during the execution of a particular function or invocations below that? Is there a way to unset them without unsetting other variables with the same names that are held by files that source the script in question?
The answer might very well be “no”, but after looking around and not finding much other than “keep track of variable names and unset anything after you’re done using it” (which will inevitably lead to a costly mistake), I figured I’d ask.
Phrased another way: is there a way to make/hack something that works like a third scope in Bash? Something between “local to a function” and “visible to everything running in this file and any files sourced by this one”?
The following is untested.
You can save a lot of your variables like this:
or save them based on a common prefix by changing the next to last line above to:
Then you can unset the ones you need to:
or unset them based on a common prefix:
To restore the variables:
Beware that
declare -pcommand will return all variables.Another technique that’s more selective might be that you know specifically which variables are used in the current function so you can selectively save and restore them:
Using variable names instead of “var”, “index”, “names” and “values” that are unlikely to collide with others. Use
exportinstead ofdeclareinside functions sincedeclareforces variables to be local, but then the variables will be exported which may or may not have undesirable consequences.Recommendation: replace the mess, use fewer globals or use a different language.
Otherwise, experiment with what I’ve outlined above and see if you can make any of it work with the code you have.