I am trying to access environment variables using Powershell. The environment variables, which is out of my control, contain periods in this format:
ENVIRONMENT_VARIABLE.1
Therefor, upon assigning the value of the environment variable to a local variable, it looks like this:
$myvar = $env:ENVIRONMENT_VARIABLE.1
However, every time I try to retrieve this variable, it always leaves out the ".1", returning nothing.
I'm aware periods + Bash = no bueno, but again, this is out of my control and I need a work around. If it helps, I already tried this as well with no luck:
$myvar = ${env:ENVIRONMENT_VARIABLE.1}
I appreciate any and all help on this. Thanks!
The last format you're using works for me:
PS> ls env:\fo*
Name Value
---- -----
Foo.1 bar.1
PS> ${env:foo.1}
bar.1
Related
I'm learning PowerShell so please forgive (what I'm sure is) a simple question.
I'm used to coding BATCH scripts and if I wanted to merge %USERDOMAIN% and %USERNAME% I would:
set zFullUsername=%USERDOMAIN%\%USERNAME%
echo %zFullUsername%
How can I do the same in PowerShell?
Thank you for your time.
On a supported Operating System, I wouldn't even bother with environment variables for this:
$zFullUsername = whoami
Then just access it as required:
$zFullUsername
In PowerShell, you can access environment variables in a few different ways. The way I recommend is to use the $env:VAR variable to access them.
$user = $env:USERNAME
$domain = $env:USERDOMAIN
echo "$domain\$user"
Note: \ is not an escape character in the PowerShell parser, ` is.
Similarly to rendering the echo command (echo is an alias of Write-Output btw) you can create a username variable like so:
$fullUserName = "$domain\$user"
Or you can skip right to creating $fullUserName straight from the environment variables:
$fullUserName = "${env:USERDOMAIN}\${env:USERNAME}"
Note: When variables have non-alphanumeric characters in them, the ${} sequence tells PowerShell everything between the ${} is part of the variable name to expand.
It seems the : in $env:VAR is actually an exception to this rule, as"Username: $env:USERNAME" does render correctly. So the ${} sequence above is optional.
To avoid confusion when trying to apply this answer in other areas, if you needed to insert the value of an object property or some other expression within a string itself, you would use a sub-expression within the string instead, which is the $() sequence:
$someVar = "Name: $($someObject.Name)"
When using either ${} or $(), whitespace is not allowed to pad the outer {} or ().
While using powershell I struggle to build up a filename from two variables. When I originally creaded the powershell script, it was working fine. Now I have tried to move some repeatable steps into a function, but the string behaviour is different.
MWE:
$topa = "ABC"
$topb = "XYZ"
function Test-Fun{
param(
$a,
$b
)
echo "$($a)H$($b).csv"
}
echo "$($topa)H$($topb).csv"
Test-Fun($topa, $topb)
The output on my system is
ABCHXYZ.csv
ABC XYZH.csv
Originally, I wanted to use an underscore instead of H and thought that is causing issues, but its not. What did I miss or rather what is the difference between string expansion within a function and outside of it?
You are calling Test-Func wrong. The comma after $topa will create an array, so you basically pass []"ABC", "XYZ" as an array to $a. In that case $b is empty!
You can easily fix this by removing the comma (also the parentheses are not necessary):
Test-Fun $topa $topb
I know I can dot source a file:
. .\MyFunctions.ps1
But, I would like to dot source the commands in a string variable:
. $myFuctions
I see that this is possible:
.{$x=2}
And $x equals 2 after the script block is sourced.
But... .{$myFunctions} does not work.
I tried $myFunctions | Invoke-Expression, but it doesn't keep the source function in the current scope. The closest I have been able to come up with is to write the variable to a temporary file, dot source the file, and then remove the file.
Inevitably, someone will ask: "What are you trying to do?" So here is my use case:
I want to obfuscate some functions I intend to call from another script. I don't want to obfuscate the master script, just my additional functions. I have a user base that will need to adjust the master script to their network, directory structure and other local factors, but I don't want certain functions modified. I would also like to protect the source code. So, an alternate question would be: What are some good ways to protect PowerShell script code?
I started with the idea that PowerShell will execute a Base64-encoded string, but only when passed on the command line with -EncodedCommand.
I first wanted to dot source an encoded command, but I couldn't figure that out. I then decided that it would be "obfuscated" enough for my purposes if I converted by Base64 file into a decode string and dot sourced the value of the string variable. However, without writing the decoded source to a file, I cannot figure out how to dot source it.
It would satisfy my needs if I could Import-Module -EncodedCommand .\MyEncodedFile.dat
Actually, there is a way to achieve that and you were almost there.
First, as you already stated, the source or dot operator works either by providing a path (as string) or a script block. See also: . (source or dot operator).
So, when trying to dot-source a string variable, PowerShell thinks it is a path. But, thanks to the possibility of dot-sourcing script blocks, you could do the following:
# Make sure everything is properly escaped.
$MyFunctions = "function Test-DotSourcing { Write-Host `"Worked`" }"
. { Invoke-Expression $MyFunctions }
Test-DotSourcing
And you successfully dot-sourced your functions from a string variable!
Explanation:
With Invoke-Expression the string is evaluated and run in the child scope (script block).
Then with . the evaluated expressions are added to the current scope.
See also:
Invoke-Expression
About scopes
While #dwettstein's answer is a viable approach using Invoke-Expression to handle the fact that the function is stored as a string, there are other approaches that seem to achieve the same result below.
One thing I'm not crystal clear on is the scoping itself, Invoke-Expression doesn't create a new scope so there isn't exactly a need to dot source at that point...
#Define your function as a string
PS> $MyUselessFunction = "function Test-WriteSomething { 'It works!' }"
#Invoke-Expression would let you use the function
PS> Invoke-Expression $MyUselessFunction
PS> Test-WriteSomething
It works!
#Dot sourcing works fine if you use a script block
PS> $ScriptBlock = [ScriptBlock]::Create($MyUselessFunction)
PS> . $ScriptBlock
PS> Test-WriteSomething
It works!
#Or just create the function as a script block initially
PS> $MyUselessFunction = {function Test-WriteSomething { 'It works!' }}
PS> . $MyUselessFunction
PS> Test-WriteSomething
It works!
In other words, there are probably a myriad of ways to get something similar to what you want - some of them documented, and some of them divined from the existing documentation. If your functions are defined as strings, then Invoke-Expression might be needed, or you can convert them into script blocks and dot source them.
At this time it is not possible to dot source a string variable.
I stand corrected! . { Invoke-Expression $MyFunctions } definitely works!
I have a batch file that runs off a powershell command I created, and I want to make it so you can pass a variable from the PS command to a website url query..
My problem is that the variables I pass from PS are referenced in batch as %1, %2, %n
now I set these variables at the beginning to more meaningful named variables, but in my url eg: www.google.com/myQuery%20has%20spaces would print out my %2 variable from PS instead of a space.
Is there anyway to clear out the %1,%2 variables that are passed? or any work around?
edit: I have tried a simple set %1= to try and set it as a null variable but it didn't work.
You can escape the % in the string with a %% in batch, which solves the issue.
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
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).
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
rerun and stej both helped me out.
I still want to source in the file using ". file.ps1" but changing "$emails=foo#yahoo.com" in my load_a_bunch_of...ps1 file to "$global:emails=foo#yahoo.com" 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.
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