Why doesn't shell %variable% work when set from PowerShell? - powershell

I am inserting a variable string in my PATH variable. I set the variables in following manner:
$var="MyTestPath"
$mypath=[environment]::GetEnvironmentVariable("PATH",[system.environmentvariabletarget]::User)
[environment]::SetEnvironmentVariable("TEST",$var,[system.environmentvariabletarget]::User)
[environment]::SetEnvironmentVariable("PATH",$mypath+";%TEST%",[system.environmentvariabletarget]::User)
The above code doesn't work for me. %TEST% variable doesn't expand itself when I check the path in the new shell. It shows new path ending with %TEST%. This has always worked when I set this from GUI or from Windows shell prompt. Why is this behavior different when variables are set from PowerShell? Is this feature removed in PowerShell?
I don't want to do the following, because it will keep adding my variable to path everytime I run the script.
[environment]::SetEnvironmentVariable("PATH",$mypath+";"+$var,[system.environmentvariabletarget]::User)

Try change this line:
[environment]::SetEnvironmentVariable("PATH",$mypath+";%TEST%",[system.environmentvariabletarget]::User)
with:
$test =[environment]::GetEnvironmentVariable("test","user") # this retrieve the rigth variable value
[environment]::SetEnvironmentVariable("PATH", $mypath +";$test",[system.environmentvariabletarget]::User)
%test% have no meaning in powershell, can't be expandend as in CMD.
$env:test retrive only from system environment variable and not from user

You're wanting to effectively set a registry value (that corresponds to a env var) that uses REG_EXPAND_SZ. See this post for details on how to do that.

Related

Assignment of environment variables in Dockerfile as a result of a command

I'm trying to asign the result of a powershell command to MY_VAR environment variable. I have tried several ways but I cannot get the variable to take the value of the operation. If I assign the variable as follows what I get as a value is the command literally.
ARG MY_ARG="VALUE"
ENV MY_VAR=[Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG))
ENV MY_VAR2=$([Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG)))
When I check the values into the container I get this:
Get-Childitem -Path Env:MY_VAR*
Name Value
---- -----
MY_VAR [Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes(VALUE))
MY_VAR2 $([Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes(VALUE)))
The base of my containers are Windows Server Core and my shell is powershell.
See this SO post , it contains several workarounds to achieve that you want.
ENV treat value as a simple text string.
UPDATE:
According to answers and comments here:
1) RUN $profile that would show you location of environment profile. Get more familiar with this file.
2) Try RUN $env.MY_VAR = [Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG)) >> $profile or another way to append the command to file. I am not familiar enough with powershell, thus be aware that you maybe would to fix slightly the command. (Comment with the right command and I will fix it for next viewers.)
3) Try to read MY_VAR in the container. If all is right, then Hoooray!, else check in the $profile that you actually get the right string of setting the variable.

Windows $env:path ="$($env:path);." where was it added?

I "fixed" a problem by running $env:path ="$($env:path);." from PowerShell. Apparently it added the current directory to my path. Which path variable did it add to please? In my environment variables dialogue, where would I see it added? User variables? System variables?
I'm confused because I had already added the folder to system variables path, but couldn't run the contained script until running ``$env:path ="$($env:path);."
Updates to $env:EnvVarName affect the current process only - no persistent changes via the registry are made:
$env:EnvVarName = 'foo'
is the equivalent of calling .NET method System.Environment.SetEnvironmentVariable as follows:
[Environment]::SetEnvironmentVariable('EnvVarName', 'foo', 'Process')
That is, the scope of the update is the current process.
Only if you substitute 'User' or 'Machine' for 'Process' in the above call (supported on Windows only[1]) do you persistently update environment variables in the registry (for the current user or the local machine (all users), respectively), for future sessions (processes)[2].
As of PowerShell [Core] 7.2, there is no PowerShell-native way to persistently update environment variables, but introducing one is being discussed on GitHub.
In other words: if you want to update not only the registry-based definition but also the value in the current process, you need to make both calls; e.g., for the current user:
# Windows only: Update / create a persistent definition for the current user,
# stored in the registry.
[Environment]::SetEnvironmentVariable('EnvVarName', 'foo', 'User')
# Update value for current process too.
$env:EnvVarName = 'foo'
Or, more in the spirit of DRY:
'User', 'Process' | foreach {
[Environment]::SetEnvironmentVariable('EnvVarName', 'foo', $_)
}
If the new value is to be based on the existing one from a given registry scope, retrieve the scope-specific value via System.Environment.GetEnvironmentVariable; e.g.:
# Get the registry-based *user* value
[Environment]::GetEnvironmentVariable('Path', 'User')
Caveat: Non-support for Windows environment variables based on REG_EXPAND_SZ registry values:
On Windows, persistently defined environment variables may be defined based on other environment variables, namely if the underlying registry value defining the variable is of type REG_EXPAND_SZ.
As of .NET 6, the System.Environment type's methods do not (directly) support such environment variables:
On getting such a variable's value, its expanded form is invariably returned; that is, references to other environment variables such as %SystemRoot% are replaced by their values.
On setting environment variables, REG_SZ registry values are invariably created, i.e. static, verbatim values - even when updating an existing REG_EXPAND_SZ value.
While quietly converting REG_EXPAND_SZ environment variables to static REG_SZ ones may often have no ill effects (as long as the new value only contains literal values), it certainly can: for instance, say a variable is defined in terms of %JAVADIR%; if that variable is converted to a static value based on the then-current value of %JAVADIR%, it will stop working if the value of %JAVADIR% is later changed.
Unfortunately, retrieval of raw REG_EXPAND_SZ environment variables and proper updating of their values currently requires direct registry access, which is quite cumbersome (not even the Windows API seems to have support for it) - see this answer.
Important considerations for the Path environment variable ($env:PATH) on Windows:
The Path environment variable is special in that it is a composite value: when a process starts, the in-process value is the concatenation of the Machine (local machine, for all users) value and the User (current user) value.
Note that since the machine-level value comes first, its entries take precedence over the user-level value's entries.
Therefore, if you want to modify (append to) the existing Path, it's better not to define the new value simply by appending to the existing in-process value ($env:Path), because you'll be duplicating the Machine or User values, depending on which scope you target.
Instead, retrieve the existing value from the target scope selectively, modify that value (typically by appending a directory, and then write the modified value back to the same scope.
To robustly make the same modification effective in the current process too is nontrivial, given that the in-process copy of $env:Path may have been modified; however, in the simple case of appending a new directory to the user's path, you can simply do $env:Path += ';' + $newDir; you may get away with this simple approach in other cases too, but note that the behavior may be different, given that the order in which the directories are listed in $env:Path matters.
Important: The Path environment variable on Windows is REG_EXPAND_SZ-based by default, so the caveats re the quiet conversion to a static REG_SZ-based value that the code below below performs apply - again, see this answer for a proper, but much more complex solution.
Example:
# New dir. to add to the *user's* path
$newDir = 'c:\foo\bin'
# Get current value *from the registry*
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
# Append the new dir and save back to the registry.
[Environment]::SetEnvironmentVariable('Path', ($userPath + ';' + $newDir), 'User')
# To also update the current process, append $newDir to the in-process
# variable, $env:Path
$env:Path += ';' + $newDir
As an aside: On Unix-like platforms, the separator is :, not ; (reflected in [System.IO.Path]::PathSeparator , and the case-sensitive variable name is Path. As stated, .NET fundamentally doesn't offer persistent environment-variable definitions on Unix-like platforms (as of .NET Core 3.1), because the various platforms have no unified native mechanism for doing so.
[1] On Unix-like platforms, targeting User or Machine is quietly ignored as of .NET Core 3.1
[2] Caveat: New processes created directly by the current PowerShell session (direct invocation, Start-Process, Start-Job) do not yet see the registry changes, because the inherit the current session's environment.
If you take apart that command, you are assigning a new string to the $env:Path variable. The string is:
"$($env.Path);."
When you place a $ followed by a set of parens () within a double-quoted string, it causes PowerShell to evaluate the contents of the parens and put the output of that evaluation into the string. See the help for about_Quoting_Rules in PowerShell help, or here in the section about evaluating expressions. Therefore:
PS C:> $A = "abc"
PS C:> $B = "AB$($A)CD"
PS C:> $B
ABabcCD
So the command you posted is appending ";." to the end of your path. When doing this, the "." will automatically be expanded out to the current directory, so effectively you will be adding a semi-colon plus the current directory to your $env:PATH variable each time you run it.
Hope this helps.
It added to $env:PATH at the process scope, meaning it's not set as a user variable or machine variable and the new value does not exist outside of your current PowerShell session. You will not see an environment variable set in this way from the Environment Variables dialog under System Properties.
If you do want to set a persistent environment variable at the User or Machine scope from PowerShell, you can, but you have to call a special method for this from the Environment class, SetEnvironmentVariable:
# Set system (machine) level environment variable
[Environment]::SetEnvironmentVariable( 'VARIABLE_NAME', 'VARIABLE_VALUE', [EnvironmentVariableTarget]::Machine )
# Set user level environment variable
[Environment]::SetEnvironmentVariable( 'VARIABLE_NAME', 'VARIABLE_VALUE', [EnvironmentVariableTarget]::User )
You can also use this method to set a Process level environment variable, but you can already do this with the $env:VARIABLE_NAME = 'VARIABLE_VALUE' syntax which is idiomatic to PowerShell.
I'm confused because I had already added the folder to system variables path
What probably happened here is that you opened a PowerShell session, then went to the Environment Variables dialog, and set the variable value. Problem is, normally environment variables, including PATH, are only read when the process starts. Most of the time, you just need to restart your PowerShell session to get the new values.
If you have Chocolatey installed, you can use the refreshenv command, which reads the current stored environment variables from the registry, and re-sets the variables in the current process. If you want to implement this sort of thing yourself, here is the source for it. Though it's written as a cmd script, you can reimplement the logic in PowerShell yourself, or just copy the script source yourself to use.
Also, don't add . to your path. While convenient, it circumvents built in PowerShell security measures. Add the directory with your programs/scripts you want to run by command to your PATH directly, or invoke them from the current directory by prepending the command with .\. For example:
.\My-ScriptInThisDirectory.ps1

Display a nicely formatted path in PowerShell have code how to format for profile

I found a snippet of code that if I paste in PowerShell It displays all of my windows path variables on one line. What would the syntax be for adding this code to my profile?
Push-Location env:
(ls path).value.split(";")
Pop-Location
Put this function in your PowerShell profile:
function Get-Path {
$env:Path.Split(";")
}
After this function is defined, you can type Get-Path to see the list of directories in your Path.
If you don't already have a profile script, run the command help about_Profiles for more information.
Not sure why you'd prefer to see PATH variable on one line, but here's the code to do it.
C:>(ls Env:\Path).value
I prefer separate lines:
C:>(ls Env:\Path).value.split(';')
As far as your PowerShell Profile goes, open PowerShell and run:
C:>$profile
It will tell you the path to your profile (make the file and directory if it doesn't exist).
Then, copy + paste the code above into it.
It will run whenever you open powershell.

Creating files at PSModulePath in batch

I am currently trying to write a batch program that installs a module named SetConsolePath.psm1 at the correct location. I am a beginner with Batch and I have absolutely no powershell experience.
Through the internet, I have learned how to display PSModulePath with powershell -command "echo $env:PSModulePath.
How can I, via .bat file, move SetConsolePath.psm1 from the desktop to the location displayed by powershell -command "echo $env:PSModulePath?
Thank you in advance, and I apologize for my lack of experience.
Before I answer, I must out that you do not want to copy PowerShell module files directly to the path pointed by PsModulePath. You really want to create a folder inside PSModulePath and copy the files there instead.
The prefix env in a Powershell variable indicates an environment variable. $env:PSModulePath is actually referring to the PSMODULEPATH environment variable. On the command line, and in batch files, environment variables can be displayed by placing the name between percent symbols. (In fact, you could have displayed this value by typing echo %PSMODULEPATH% instead.)
To reference the desktop folder, have a look at this answer, which shows you how to use another environment variable, USERPROFILE.
Therefore, to copy the file from the desktop directory to the path specified in PSModulePath, you would do this:
COPY "%USERPROFILE%\Desktop\SetConsolePath.psm1" "%PSMODULEPATH%"
And, as I warned earlier, you really should copy the file to a folder underneath PsModulePath. So what you really want is:
IF NOT EXIST "%PSMODULEPATH%\MyNewFolder" MKDIR "%PSMODULEPATH%\MyNewFolder"
COPY "%USERPROFILE%\Desktop\SetConsolePath.psm1" "%PSMODULEPATH%\MyNewFolder"

'findstr' is not recognized as an internal or external command,

I got the following error while starting JBoss from a command line prompt today:
'findstr' is not recognized as an internal or external command
Please google it, you can find a lot of answers. But do as below to fix it. Add the following value to Right Click My Compuer -> Advanced -> Environment Variables -> System Variables -> Select Path variable -> append the below value.
C:\WINDOWS\system32
It should work with that change.
As others pointed, issue is in wrong settings of PATH variable in Windows.
According to article this is most probably because some stupid installer wrongly modified PATH variable in Windows registry. Registry has 2 different string value types - REG_SZ and REG_EXPAND_SZ. Only the second one allows for expansion of %SystemRoot%.
So check your path by typing set path in command prompt. If you see unexpanded %SystemRoot% and other variables in Path, you are affected (PATH should show only plain directory names, not variables).
You need to edit Path variable in registry: HKEY_CURRENT_USER\Environment and HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment. As it is not possible to change the type of key, save the path value somewhere, delete the key and re-create it with type REG_EXPAND_SZ. You need to logout for changes to take effect.
for me it works when I've coped findstr(from windows/system32) to wildfly/bin
Please go throught the simplest steps:-
go to C:\Windows\system32\ and copy findstr.exe file.
paste this file into the location C:\Program Files\Java\jdk1.6.0_24\bin
Run your jboss again you will get out of this.....
Check to see if you %SystemRoot% is evaluating (type set path into a command prompt, you should not see %SystemRoot%, but instead that actual path). If your path variable's (user, or systems) first entry begins with an %(an environment variable) this can cause an issue.
To resolve this, simply swap this first entry with anything else in your path that does not lead with an environment variable.
You can also hard code the directory by replacing 'findstr' with 'C:\Windows\system32\findstr'. This is useful when using systems with restricted user permissions.
I have try to work with play framework but stuck with to run activator.bat file but solution is the same just copy file from windows/system32/findsr and past it to under stuck folder then run the respective file again.
thanks to andrewsiand Suryaprakash
Please beware that current Windows systems use a Capital "S" for the System directory, so:
C:\WINDOWS\System32
%SystemRoot%\System32
Omitting the capital S will result in a neglect of the line in the %PATH%
In my case (not JBoss related) the following helped to fixed this error.
Instead of:
SET path="%path%;C:\some\additional\path"
I used:
SET "path=%path%;C:\some\additional\path"
For IBM ACE solution for
'findstr' is not recognized as an internal or external command,
Go to the path C:\Windows\System32
Find the findstr.exe, copy it and then find the path where you bin file of your application is found. eg C:\Program Files\IBM\ACE\11.0.0.12\server\bin then past it inside the bin file
cancel the console of ace and re-open it.
Then run ACE toolkit command on ace console.
Then press enter, now it can open.