Share Environment Vars between WSL and Windows

Craig Wilhite

Hey WSL users—we have more features to share with you! Our latest Windows Insider build lets you share environment variables between WSL and Windows. Starting with Build 17063, let’s look at how you can leverage the new “WSLENV” to enhance environmental variable interop between Win32/WSL.

Summary

For the pros who’ve already heard about WSLENV and just want to know how it works, see below for a quick synopsis:

  • WSLENV is a colon-delimited list of environment variables that should be included when launching WSL processes from Win32 or Win32 processes from WSL
  • Each variable can be suffixed with a slash followed by flags to specify how it is translated
  • The value is a path that should be translated between WSL paths and Win32 paths
  • The value is a list of paths. In WSL, it is a colon-delimited list. In Win32, it is a semicolon-delimited list
  • The value should only be included when invoking WSL from Win32
  • The value should only be included when invoking Win32 from WSL
  • You can set WSLENV in .bashrc or in the custom Windows environment for your user

A sample of how a WSLENV could possibly look:

WSLENV=GOPATH/l:USERPROFILE/w:SOMEVAR/wp

What are Environment Variables?

Environment variables are a way to store configurable values across your entire system—all your programs have access to these. On Windows, you can find your environmental variables by going to “Edit environment variables for your account” in the Control Panel. It will show a window like the one below:

Notice the wide range of values in the example above—lists of directories, software versions, locations where temporary files are permitted to be stored, and more.   Who sets these values? You can, newly installed software on your system can, or Windows has set some of them.

An example of a well-known environmental variable is PATH. Much like the name implies, PATH contains a list of directories (paths) separated by a semi-colon which point to the locations of numerous executables. Here’s a common example of how PATH is used:

I’m about to invoke Node from the command line. When I enter this command, cmd reads the contents of PATH, searching for a directory that contains the executable “node.exe”. If it finds it, it stop the search immediately and invokes the executable it found. The image above has PATH printed to the console—and you’ll notice that the last entry in PATH points to a directory which does in fact contain Node.

WSL and environment variables — before Insider 17063

Referencing environment variables from *both *Windows and WSL introduces interop issues. Prior to 17063, the only Windows environment variable that WSL had access to was PATH (so you could launch Win32 executables from under WSL). But there was no way to set an environment variable in WSL, invoke a Win32 process, and expect for that variable to be fed through to the process.

Given these limitations, you couldn’t pass an environment var in WSL to a Win32 process without jumping though convoluted hoops. Since passing environment vars to processes is a common practice and we received plenty of feedback concerning how this was a pain  point for our users, we wanted to make this a better experience.

WSL and environment variables — after Insider 17063

We’re introducing a special environmental var named WSLENV to assist in the flow between WSL and Win32. WSLENV is shared; it exists in both environments. A user sets the value of WSLENV to a concatenation of several other already-defined environment vars  , each suffixed with a slash followed by flags to specify how the value should be translated.  An example definition could be something like this:

WSLENV=HOME/w:GOPATH/l:TMPDIR/p …

There are four flags you can use to influence how they’re translated. Let’s walk through each:

/p

This flag indicates that a path should be translated between WSL paths and Win32 paths. Notice in the example below how we set the var in WSL, add it to WSLENV with the ‘/p’ flag, and then read the variable from cmd.exe and the path translates accordingly.

/l

This flag indicates the value is a list of paths. In WSL, it is a colon-delimited list. In Win32, it is a semicolon-delimited list. See in the example below how PATHLIST is appropriately converted to a semi-colon separated list.

/u

This flag indicates the value should only be included when invoking WSL from Win32. In the example below, we set FORWSL from cmd and it will show up in WSL.

Notice how it does not convert the path automatically—we need to specify the /p flag to do this. We can combine flags and do just that:

/w

This flag indicates the value should only be included when invoking Win32 from WSL.

Passing vars with a script

If you’re writing a bash script and want to pass environment variables to some process that you will be invoking, you can do this:

#!/bin/bash

#some logic here...

export MYPATH=/mnt/c/Users/

#invoke the process and pass the env var in one go:
WSLENV=$WSLENV:MYPATH/p process.exe

One important thing to call out here is that I am appending the environment variable I want to pass to the process to WSLENV. We recommend this so you don’t wipe out the other vars previously set for WSLENV.

An Example with Go

For example, say you want to set up a dev environment with Go in WSL. With the WSLENV var, we can configure it to share GOPATH between WSL and Win32. If you’ve never used Go before,  if your project directory is outside of the root directory where Go is installed then you need to set GOPATH. Let’s walk through an end-to-end example.

1. Install Go on Windows & WSL

First, we need to install Go on both platforms. To install on Windows, grab Go and select Windows as your platform. Click through and complete the installation. Next, install Go in WSL. We need to issue this command from WSL:

sudo apt-get install golang

2. Grab VSCode

Next, we’ll want to install VSCode in Windows. If you don’t already have VSCode, you can get it from here.

3. Set up the project

Next, we need to set up our Go project. The project needs to be under the Windows file system (I’ll just make mine on my Desktop). Issue the following commands in PowerShell:

mkdir $env:USERPROFILE\desktop\goProject
cd $env:USERPROFILE\desktop\goProject
New-Item hello.go

Open the newly created hello.go file with VSCode or your favorite text editor and add this text to it:

4. Set Environment Variables

Now we need to set our GOPATH to point to this project, and then add GOPATH to WSLENV. From PowerShell:

setx GOPATH "$env:USERPROFILE\desktop\goProject"
setx WSLENV "$env:WSLENV:GOPATH"/p

5. Success

That’s all you need to do. If you open the project file in VSCode, it will prompt you about missing package–click yes to install these. After they complete their installation, you will have full VSCode integration (intellisense, linting, etc). On the WSL side, now if you navigate to your project directory (it will be under /mnt/c/…) and try to “go build”, it will run successfully and output a Linux binary that you can run in WSL. Debugging from VSCode will produce a Windows executable that you can also run.

Conclusion

We hope using the new WSLENV environment variable will help with interoperating between WSL and Win32. Grab Insider Build **17063 **to try it out. We would love to get feedback on this feature. Is it too clunky? does it work the way you’d hoped? Leave a comment below and let us know.

FAQ

What happens if I set an environment var in WSLENV from Win32 which is already defined in WSL?

The definition in WSL (via .profile or other) will override the value defined in WSLENV when accessed via WSL.

What about the other way around (variable defined in Win32, then defined and shared from WSL?)

The WSLENV definition will override the value previously defined in Win32 (the opposite of the above question).

What happens if I concatenate WSLENV to the end of my PATH environment var?

It will not concatenate anything—WSLENV gets chopped off.

Do the changes I make to WSLENV from WSL persist after I close WSL?

No, you’ll need to modify the appropriate config file (.profile, .bash_rc, etc) and set WSLENV to whatever you’d like.

Can I set WSLENV to an actual filesystem path or does it have to be set to an existing environment var?

You can set the value to whatever you’d like. Although, if you were to set a filesystem path, no translation occurs. That’s why we recommend setting WSLENV to the environment variable containing the path and you’ll get the path-translation flags for free.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Dirk Seynhaeve 0

    I like the feature. I have been looking for an efficient way to trim the Window PATH variables when bringing them over from Win10 to WSL.
    Before learning about this feature, I ended up turning off path sharing in my /etc/wsl.conf (appendWindowsPath = false), and explicitly building my WSL PATH by adding /mnt/c/… locations.

    Now I can take advantage of this feature; I have, In my Windows Environment

    WSLPATH=dir1;dir2;...
    WSLENV=WSLPATH/up

    The variables makes it across when I start the WSL shell. Of course I still need to manually append ${WSLPATH} to my ${PATH}.

    However, the ‘/p’ flag I specify in Windows takes care of everything:
    * Syntax of the path names (‘C:...\’ to ‘/mnt/c/…/…’).
    * Separator of the directories (‘;’ to ‘:’).

    Which makes me wonder:
    what is the difference between the ‘/p’ flag and the ‘/l’ flag??

    I did a quick experiment with my example, and the end result in WSL looks exactly the same…

    If I may make a suggestion: the documentation above is good, but doesn’t hit the use model right away:
    1. Stream line your PATH variable going across.
    2. Provide environment variables required for applications, going across.
    3. …
    It might all be there, but it seems to be drowning in… clunky descriptions??? 🙂

Feedback usabilla icon