2

I'm attempting to save some environment variables based on the output of the command "wsl --list" using Powershell, when I debug this code it seems to be flowing as expected however when I inspect my environment variables I'm unable to find the expected keys and values.

When I use the same SetEnvironmentVariable method with any other hardcoded value it seems to work. Write-Host on $distroName results in the expected string too so I'm honestly lost on this. Any help would be appreciated! Here is my code:

$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)

my wsl --list output:

┖[~]> wsl --list
Windows Subsystem for Linux Distributions:
Debian (Default)
Alpine
dutchrebel
  • 23
  • 5
  • 2
    You probably want to use `[Environment]::SetEnvironmentVariable("SINDAGAL_DEFAULT_DISTRO",$distroName, "Machine")`. Without specifying the target, `SetEnvironmentVariable()` creates, modifies, or deletes an environment variable stored in the **current process** – Theo Oct 17 '21 at 12:42
  • Please, add your `$wslListOutput` list to the question. – iRon Oct 17 '21 at 12:46
  • On my system, code jumps into `else` branch and sets `SINDAGAL_INIT_DISTRO_=true`, although `$distroName` is not empty (it is 'UBUNTU-20.04'). Strangely enough, when hardcoding `$distroName` using the same value, it works. – zett42 Oct 17 '21 at 12:55
  • Thank you for all your comments guys! – dutchrebel Oct 17 '21 at 13:53
  • @Theo I tried adding that third parameter in, even though it doesn't error its still not saving my variables unfortunately. – dutchrebel Oct 17 '21 at 13:53
  • @iRon I've added my wsl --list output in the question as you asked! – dutchrebel Oct 17 '21 at 13:53
  • @zett42 Thats exactly whats happening for me, good to know its not an issue specific for my machine, very curious what the bug might be now! – dutchrebel Oct 17 '21 at 13:53

1 Answers1

3

Cause

This is an encoding issue. Redirected output of wsl is UTF-16 LE encoded, which is not automatically recognized by PowerShell (as of PS 7.1.5). Instead it always interprets the output using the encoding stored in [Console]::OutputEncoding, which on Windows defaults to the given system's legacy OEM code page (e.g. 437 on US-English systems).

As UTF-16 is a two-byte encoding we typically end up with embedded \0 characters (code points below 256, such as the ASCII character set). These cause the string to be clipped, when calling SetEnvironmentVariable, because the API expects null-terminated strings.

Solution

Set [Console]::OutputEncoding to match the output encoding of the process, before launching it and restore the original encoding afterwards.

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

$wslListOutput = wsl --list

[Console]::OutputEncoding = $oldEncoding

Another common output encoding used by some programs is [Text.Encoding]::UTF8.

See this answer for more in-depth information and an Invoke-WithEncoding cmdlet, which automates the process of temporarily changing and restoring the encoding.

zett42
  • 25,437
  • 3
  • 35
  • 72
  • Thank you very much guys, I solved it by using a mix of both of your approaches. Its a shame that this is an issue, I personally wouldn't be able to diagnose this and probably would end up giving the whole project up :) – dutchrebel Oct 17 '21 at 15:19
  • 1
    I think your own answer here is the best duplicate for https://stackoverflow.com/q/72411284/45375 (I've placed it first in the list). Note that the - unfortunately named - `[Text.Encoding]::Unicode` encoding represents UTF-16LE on _all_ platforms. – mklement0 May 28 '22 at 01:10