简体   繁体   English

使用PowerShell设置嵌套的可扩展环境变量

[英]Set nested expandable environment variable with PowerShell

I have read many posts on this topic and tried many things but cannot seem to get this to work. 我已经阅读了很多关于这个主题的帖子,并尝试了很多东西,但似乎无法让这个工作。 I want to set an environment variable and then nest that variable in the Path environment variable. 我想设置一个环境变量,然后将该变量嵌套在Path环境变量中。 I switched from Batch files to Powershell because I could not get late expansion working to prevent expanding nested variables already in the Path, etc. 我从批处理文件切换到Powershell,因为我无法进行后期扩展工作以防止扩展路径中已有的嵌套变量等。

Here is script to demonstrate the issue. 这是用于演示该问题的脚本。 Given that you have Maven unzipped to e:\\Apps\\maven\\apache-maven-3.2.1 location, the test script will run, create the MAVEN_HOME variable, nest that variable unexpanded in the Path, and execute the mvn --help . 鉴于您已将Maven解压缩到e:\\Apps\\maven\\apache-maven-3.2.1位置,测试脚本将运行,创建MAVEN_HOME变量,在路径中嵌套该未变量的变量,并执行mvn --help

This all works fine, except that upon opening a fresh command prompt and typing ECHO %PATH% it is clear that the change has not been applied. 这一切都很好,除了打开一个新的命令提示符并键入ECHO %PATH% ,很明显没有应用更改。

I have heard that alphabetical order of the environment variables can matter, but in this case "MAVEN_HOME" comes before "PATH" so that shouldn't matter. 我听说环境变量的字母顺序很重要,但在这种情况下,“MAVEN_HOME”出现在“PATH”之前,所以这无关紧要。

The Path variable is being created in the registry as a REG_EXPAND_SZ type. Path变量在注册表中以REG_EXPAND_SZ类型创建。

I am running the Powershell script from a batch file to avoid signing: 我正在从批处理文件运行Powershell脚本以避免签名:

Call Powershell.exe -executionpolicy bypass -File .\test.ps1

Here is the Powershell script: 这是Powershell脚本:

#Environment Variable
$HOME_VAR = "MAVEN_HOME"
$HOME_PATH = "e:\Apps\maven\apache-maven-3.2.1"
$APP_CMD = "mvn"
$APP_ARGS = "--help"

#String to be added to the Path
$BIN_PATH = "%$HOME_VAR%\bin"

#Registry location of Machine Environment variables
$SYSVAR_REG_PATH = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"

#Get the correct hive
$HKLM = [Microsoft.Win32.Registry]::LocalMachine
#Get the registry key with true to indicate that it is for editing
$sysvar_regkey = $HKLM.OpenSubKey($SYSVAR_REG_PATH, $TRUE)

#Set the value in the registry
$sysvar_regkey.SetValue($HOME_VAR, $HOME_PATH)

#Read the value back out
$HOME_PATH = $sysvar_regkey.GetValue($HOME_VAR)

#Set the value within the current process
[Environment]::SetEnvironmentVariable($HOME_VAR, $HOME_PATH, [EnvironmentVariableTarget]::Process)

#Must use RegistryKey to get value because it allows the "DoNotExpandEnvironmentNames" option
#This ensures that nested environment variables are not expanded when read
$envpath = $sysvar_regkey.GetValue("Path", "C:\Windows", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$segments = $envpath.split(";")

Write-Host "BEFORE"
Write-Host $env:path

#See if bin path is already in the Path
If (($segments -contains $BIN_PATH) -eq $FALSE) {
    #Add the bin path to the path
    $segments += $BIN_PATH
    $envpath = $segments -join ";"

    #RegistryValueKind.ExpandString ensures that variables in the path will expand when the Path is read
    $sysvar_regkey.SetValue("Path", $envpath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
}   

#Read the path value as expanded
#All nested variables in the Path are expanded
$envpath = $sysvar_regkey.GetValue("Path")

#Update the Path for the current process
#Must do this every time to expand the Path
[Environment]::SetEnvironmentVariable("Path", $envpath, [EnvironmentVariableTarget]::Process)

Write-Host "AFTER"
Write-Host $env:path

#Run the command line
& $APP_CMD $APP_ARGS | Write-Host

A new cmd session uses the path inherited from the parent process's environment (generally either Windows Explorer, or a cmd or PowerShell session that spawned it). 新的cmd会话使用从父进程的环境继承的路径(通常是Windows资源管理器,或者生成它的cmd或PowerShell会话)。 Changing the value of an environment variable in the registry doesn't automatically change Explorer's environment, so it doesn't change the value used by new cmd sessions. 更改注册表中环境变量的值不会自动更改Explorer的环境,因此它不会更改新cmd会话使用的值。

If you set an environment variable through the System Properties control panel, the value is reflected in new cmd sessions not because it's stored in the registry, but because that also changes the value in the environment of the main Explorer process. 如果通过“ 系统属性”控制面板设置环境变量,则该值将反映在新的cmd会话中,而不是因为它存储在注册表中,而是因为它还会更改主Explorer进程环境中的值。 (Note that simply opening the Environment Variables dialog box from System Properties and clicking OK updates all of Explorer's environment variables from the registry values). (请注意,只需从“ 系统属性”中打开“ 环境变量”对话框,然后单击“ 确定”即可从注册表值更新所有Explorer的环境变量)。

To achieve the same effect from PowerShell — simultaneously changing the value in the registry and in Explorer's environment which is passed on to new cmd sessions — you can do this: 要从PowerShell获得相同的效果 - 同时更改注册表中的值以及在传递给新cmd会话的资源管理器环境中 - 您可以执行以下操作:

[Environment]::SetEnvironmentVariable("Path", $envpath, 'Machine')

Note that this does not replace 请注意,这并不能取代

[Environment]::SetEnvironmentVariable("Path", $envpath, 'Process')

because if the target is Machine , the value in the current PowerShell session is not changed. 因为如果目标是Machine ,则不会更改当前PowerShell会话中的值。 (You can use the strings 'Process' and 'Machine' instead of [EnvironmentVariableTarget]::Process and [EnvironmentVariableTarget]::Machine ). (您可以使用字符串'Process''Machine'代替[EnvironmentVariableTarget]::Process[EnvironmentVariableTarget]::Machine )。


Note, BTW, that new PowerShell sessions always use the Path value from the registry rather than the one inherited from the parent. 请注意,BTW,新的PowerShell会话始终使用注册表中的Path值而不是从父级继承的值。 This behavior applies exclusively to Path ; 此行为仅适用于Path ; all other environment variables are inherited from the parent process. 所有其他环境变量都从父进程继承。 See this answer for more information. 有关更多信息,请参阅此答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM