【问题标题】:Powershell SetEnvironmentVariable not setting the variables as expectedPowershell SetEnvironmentVariable 未按预期设置变量
【发布时间】:2021-10-17 11:40:05
【问题描述】:

我正在尝试使用 Powershell 根据命令“wsl --list”的输出保存一些环境变量,当我调试此代码时,它似乎按预期流动但是当我检查我的环境变量时无法找到预期的键和值。

当我将相同的 SetEnvironmentVariable 方法与任何其他硬编码值一起使用时,它似乎可以工作。 $distroName 上的 Write-Host 也会产生预期的字符串,所以我真的迷失了。任何帮助,将不胜感激! 这是我的代码:

$wslListOutput = wsl --list
((Get-ChildItem env:*).Name | Select-String -Pattern "(SINDAGAL_INIT_DISTRO_([a-zA-Z=])*)|SINDAGAL_DEFAULT_DISTRO")
foreach ($line in $wslListOutput)
{
  $lineIsEmpty = ("" -eq $line) -or ([string]::IsNullOrWhiteSpace($line))
  $Introline = $line -eq "Windows Subsystem for Linux Distributions:"

  if($lineIsEmpty -or $Introline){ continue } 
  
  if($line -Match "(([a-zA-Z]*) ([(Default)])*)"){
     $distroName = ($line -split ' ',2)[0]    
     [System.Environment]::SetEnvironmentVariable("SINDAGAL_DEFAULT_DISTRO",$distroName)
  } else{
     $distroName = $line.ToUpper()
     $variablePath = "Env:SINDAGAL_INIT_DISTRO_${distroName}"
     [System.Environment]::SetEnvironmentVariable("SINDAGAL_INIT_DISTRO_${distroName}",$true)
  }
}

# Cannot see the variables which are supposed to be set in here at all
((Get-ChildItem env:*).Name)

我的 wsl --list 输出:

┖[~]> wsl --list
Windows Subsystem for Linux Distributions:
Debian (Default)
Alpine

【问题讨论】:

  • 您可能想使用[Environment]::SetEnvironmentVariable("SINDAGAL_DEFAULT_DISTRO",$distroName, "Machine")。在不指定目标的情况下,SetEnvironmentVariable() 创建、修改或删除存储在当前进程中的环境变量
  • 请将您的$wslListOutput 列表添加到问题中。
  • 在我的系统上,代码跳转到else 分支并设置SINDAGAL_INIT_DISTRO_=true,尽管$distroName 不是空的(它是'UBUNTU-20.04')。奇怪的是,当使用相同的值对$distroName 进行硬编码时,它可以工作。
  • 感谢你们所有的 cmets 伙计们!
  • @Theo 我尝试在其中添加第三个参数,即使它没有出错,但不幸的是它仍然没有保存我的变量。

标签: powershell windows-10 environment-variables


【解决方案1】:

原因

这是一个编码问题。 wsl 的重定向输出是 UTF-16 LE 编码的,PowerShell 不会自动识别(从 PS 7.1.5 开始)。相反,它总是使用存储在 [Console]::OutputEncoding 中的编码来解释输出,在 Windows 上默认为给定系统的旧版 OEM 代码页(例如,美国英语系统上的 437)。

由于 UTF-16 是一种两字节编码,我们通常以嵌入的 \0 字符结尾(低于 256 的代码点,例如 ASCII 字符集)。在调用 SetEnvironmentVariable 时,这些会导致字符串被剪裁,因为 API 需要以 null 结尾的字符串。

解决方案

在启动之前设置[Console]::OutputEncoding以匹配进程的输出编码,然后恢复原始编码。

$oldEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = [Text.Encoding]::Unicode  # on Windows: UTF-16 LE

$wslListOutput = wsl --list

[Console]::OutputEncoding = $oldEncoding

某些程序使用的另一种常见输出编码是[Text.Encoding]::UTF8

请参阅 this answer 了解更深入的信息和 Invoke-WithEncoding cmdlet,它可以自动执行临时更改和恢复编码的过程。

【讨论】:

  • 我猜正确的解决方法是使用:[Console]::OutputEncoding = [Text.Encoding]::Unicode
  • 非常感谢你们,我通过混合使用你们的两种方法解决了这个问题。很遗憾这是一个问题,我个人无法诊断这个问题,可能最终会放弃整个项目:)
  • @iRon 谢谢,我添加了OutputEncoding 解决方法。对于 OutputEncoding 不适合的情况,我仍然保留原来的解决方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-02
相关资源
最近更新 更多