tags:

views:

182

answers:

4

I have a variable that is common to most of my app called "emails". I also want to use "emails" as the name of a parameter in one of the scripts. I need to refer to the value of both variables in the same script. Ideally there would be a way to refer using module/namespace or something and perhaps there is but I don't know it. You can see how I hack around this but it is ugly and prone to error. Is there a better way?

# PowerShell v1
# Some variable names are very common.
param ($emails)

# My Hack
# We need to save current value so we have it after we source in variables below.
$emails0=$emails

# Below is going to load a variable called "emails" which will overwrite parm above.
. C:\load_a_bunch_of_global_variables.ps1
+4  A: 

It is because as documentation says: (the dot sourcing operator) Runs a script so that the items in the script are part of the calling scope.

In this case I would convert C:\load_a_bunch_of_global_variables.ps1 to a module and pass $emails as parameter or export a function that sets the $script:emails variable in the module. Then the variable will not be in a conflict with the variable in the parent script.

For more information about modules you can use get-help about_modules.


I would avoid using global variables if possible in my scripts.

Why? Because it is a code smell (as programmers say). With one script there is no problem. If two scripts use the same global variable and only read, it is maybe acceptable. But if any of them changes the value, then there might be unpleasant conflicts.

In some cases Get-Variable -scope 1 -name myvariable would help, but I would use it only in closed pieces of code like modules or in short scripts (the same reason as with global variables).

stej
Don't think those are available in v1. I am just picking this up but fear I would have more trouble getting v2 installed where I need it so I am sticking to v1 for now.
Ethan Post
No, modules are note available in V1. However, V2 brings many new features and it is definitely worth upgrading to V2 ;)
stej
Thanks. I am aware of the problems with global variables but they really are either required or I would need to write an api to fetch them and that complicates the design. I am porting an application from ksh to PowerShell. In ksh I don't care about pretty command line names because in ksh I would use "-e" instead of "-emails" and "-p_emails" or something like that just looks dumb. Trying to get the right mix of readability and elegance here. Most of the variables have no conflict as the global's are usually quite unique.
Ethan Post
A: 

You can alwways access your global variables from a script using $global:var name inside your script you have local scope and you won't get collisions. If you . source your script you will override the global var.

For Ex if a have a script

$Crap ="test"
$Crap

And you run the flowing commands you get what you want. In line 2 we run the script and the var doesn't get a conflict but if you run the script as in line 4 with a . source you get what you are discovering which due to the way the . operator works

1:PS C:\Users\Adam> $crap = "hi"
2:PS C:\Users\Adam> .\test.ps1
test
3:PS C:\Users\Adam> $crap 
hi
4:PS C:\Users\Adam> . .\test.ps1
test
5:PS C:\Users\Adam> $crap
test
6:PS C:\Users\Adam>

if You add the following line to the script run it

$global:crap;

you will get

PS C:\Users\Adam> .\test.ps1
test
hi
rerun
+1  A: 

rerun and stej both helped me out.

I still want to source in the file using ". file.ps1" but changing "[email protected]" in my load_a_bunch_of...ps1 file to "$global:[email protected]" solved the problem. I can now refer to the variable using global key word when I have a local and a global variable, and when there is only one variable to deal with I can leave out the global keyword.

Ethan Post
+1  A: 

While you can use Get-Variable -scope to get access to variables at arbitrary levels of the call stack, it is easier in this case to grab the top level (to the script) variable using the script: modifier e.g.

$script:emails
Keith Hill
Keith, I'm not sure if we both understand it in the same way.. Just try this: "param(`$emails)`r`n`$emails`r`n`$script:emails`r`n. .\s2.ps1`r`n`$emails`r`n`$script:emails" > s1.ps1; '$emails="from s2"' > s2.ps1; .\s1.ps1 test. The output is test test from s2 from s2
stej
Oh I see - collision of variable names at the script scope. FWIW I also tend to create script with a bunch of variables defined in it (BuildVars.ps1) when I tend to have a bunch of different scripts (build scripts) that need the same data. So while I eschew globals when programming in C++, I fudge that rule a bit when scripting in PowerShell.
Keith Hill