文章目录
- 命令行中获取环境变量取值
- 获取不展开的值
- 具体实现
- 注解
- 封装为函数
- 版本1
- 版本2
命令行中获取环境变量取值
- 这里主要讨论获取未展开的值
- 本来获取未展开的值应该作为默认选项,至少有合适的api方便直接调用,但是不知道微软怎么想的,让这个任务变得不直接
获取不展开的值
-
为了便于讨论和实验,下面假设环境变量都讨论的是当前用户级别的环境变量
-
意思是假设你的环境变量中有一个
exes
变量,并假设对应的取值为C:\exes
-
现在某个变量比如
Path
,或者其他任何引用了exes
的环境变量(例如 创建环境变量testx
,可以通过语句setx testx %exes%
) -
在使用命令行查询
testx
时查询到的值不是%exes%
,而是其展开值C:\exes
,这会造成许多间接引用的路径发生更改后无法自动更新,造成灵活性下降,特别是通过命令行修改(尤其是添加)多取值环境变量的部分值的时候- 比如
%exes%
被改为C:/new/exes
,但是由于添加新值前需要获取旧值,旧值中的%exes%
如果被替换为C:\exes
,那么在为testx
添加新值时丢失%exes%
,取而代之的是C:\exes
,然而这个值不是我们想要的,因为它是死板的,不会随着%exes%
的更新而自动更新
- 比如
-
此外,这里的不展开环境变量主要值的是记录着路径的环境变量)
-
总之,我们需要解决的问题是命令行获取不展开的值
-
目前的方法是通过访问注册表来获取不展开的值
-
相关注册表
-
'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' #系统级 'HKCU:\Environment' #当前用户级
-
-
相关命令
reg query
Get-item
具体实现
推荐powershell的Get-item
,即(gi
)命令来获取
#以'HKCU:\Environment'为例
# 准备用语测试的一对值
setx exes 'C:\exes1'
setx testx %exes1%#为了获取非展开的原值,可以直接用Get-Item访问'HKCU:\Environment',也可以分为两部分访问路径
#考虑到全局/用户级别环境变量路径都是以environment结尾,这样可以更灵活和选择判断全局还是用户级别
$Name='testx'
#$registerKeyParent=Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
$registerKeyParent=Get-Item -Path 'HKCU:'#获取目标路径的父路径
$RegisterKey = $registerKeyParent.OpenSubKey('Environment') #打开指定子键$registryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames$RegisterKey.GetValue($Name, $null, $registryValueOption)
相关api如下
RegistryKey.GetValue 方法 (Microsoft.Win32) | Microsoft Learn
PS> $envRegisterKey.GetValueOverloadDefinitions
-------------------
System.Object GetValue(string name)
System.Object GetValue(string name, System.Object defaultValue)
System.Object GetValue(string name, System.Object defaultValue,
Microsoft.Win32.RegistryValueOptions options)
注解
使用此重载指定检索到的值的特殊处理。 例如,可以在检索类型的RegistryValueKind.ExpandString注册表值时指定RegistryValueOptions.DoNotExpandEnvironmentNames检索字符串,而无需展开嵌入的环境变量。
defaultValue
使用 参数指定要返回的值(如果name
不存在)。
封装为函数
- 下面两个版本实现方式不同,注意参数不同,根据需要调整
版本1
来源于scoop中的函数Get-EnvVar
function Get-EnvVarRawValue
{<#.SYNOPSISRetrieve an environment variable value..DESCRIPTIONThis function retrieves the value of the specified environment variable from the registry. It can fetch both user-specific and system-wide variables..PARAMETER NameThe name of the environment variable..PARAMETER GlobalSwitch to indicate if the environment variable is global (system-wide)..EXAMPLEGet-EnvVar -Name "Path"#>param([string]$Name,[switch]$Global)# Determine the appropriate registry key to use based on the Global flag# User scope uses the HKCU hive, while global (system-wide) uses the HKLM hive$registerKey = if ($Global){# HKLM hive is used for system-wide environment variables# This is the same key used by the system Configuration Manager# when setting environment variables through the System Properties# control panelGet-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'}else{# HKCU hive is used for user-specific environment variablesGet-Item -Path 'HKCU:'}# Open the Environment sub-key off the selected registry key$envRegisterKey = $registerKey.OpenSubKey('Environment')# Retrieve the value of the specified environment variable# The DoNotExpandEnvironmentNames option is used to prevent the registry# from expanding any environment variables it finds in the value# This is necessary because environment variables can be nested (e.g. %PATH%)# and we want to return the raw, unexpanded value$registryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames$envRegisterKey.GetValue($Name, $null, $registryValueOption)
}
版本2
function Get-EnvVarRawValue
{<# .SYNOPSIS从相应的注册表中读取指定环境变量的取值.DESCRIPTION# 不会自动转换或丢失%var%形式的Path变量提取# 采用reg query命令查询而不使用Get-ItemProperty 查询注册表, 因为Get-ItemProperty 会自动转换或丢失%var%形式的变量# 注册表这里也可以区分清楚用户级别和系统级别的环境变量#>[CmdletBinding()]param ([Alias('Name', 'Key')]$EnvVar = 'new', [ValidateSet('Machine', 'User', 'Process')]$Scope = 'User')$currentValue = [System.Environment]::getenvironmentvariable($EnvVar, $Scope)if ($CurrentValue){if ($scope -eq 'User' -or $scope -eq 'Process'){$CurrentValueUser = reg query 'HKEY_CURRENT_USER\Environment' /v $EnvVar$currentValue = $CurrentValueUser}if ($scope -eq 'Machine' -or $scope -eq 'Process'){$currentValueMachine = reg query 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' /v $EnvVar$currentValue = $currentValueMachine}if ($Scope -eq 'process'){#recurse$U = Get-EnvVarRawValue -EnvVar $EnvVar -Scope 'User'$M = Get-EnvVarRawValue -EnvVar $EnvVar -Scope 'Machine'$currentValue = (@($U , $M) -join ';') -split ';' | Select-Object -Unique | Remove-RedundantSemicolonreturn $currentValue# $CurrentValue = $CurrentValueUser + $currentValueMachine}$CurrentValue = @($CurrentValue) -join '' #确保$CurrentValue是一个字符串# $CurrentValue -match 'Path\s+REG_EXPAND_SZ\s+(.+)'# $mts = [regex]::Matches($CurrentValue, $pattern)# return $mtsif ($CurrentValue -match 'REG.*SZ\s+(.+)'){$CurrentValue = $Matches[1] | Remove-RedundantSemicolon# 规范化}}if ($VerbosePreference){Write-Verbose "RawValue of [$EnvVar]:"Write-Host ($currentValue -split ';' | Format-DoubleColumn | Out-String)}# 返回的是一个字符串,而不是;分隔的字符串数组return $currentValue
}