I have been using my MacBook a lot now that it is my main computer at work. So much so that I found it necessary to invert my scroll wheel on my mouse on my windows desktop to behave like my MacBook. I've also been using
While in the process of writing the scripts to configure my bash environment on my Windows machine, I found the need to be able to access environment variables that are set in Windows. With WSL, the only environment variables that really come over to bash is PATH
.
I googled around for a bit, but didn't find any way to actually do this. Then I remembered that WSL has interop between Windows and WSL. This means that I can execute a Windows executable and redirect the output back to bash. Which means I should be able to execute powershell.exe
to get the information I need.
I first started with a test of just doing:
$ echo $(powershell.exe -Command "gci ENV:")
And that gave me what I wanted back. Now there are some differences in the paths between WSL and Windows, so I knew I would also have to adjust for that.
What I did was put a file called ~/.env.ps1
in my home path.
#!~/bin/powershell # Will return all the environment variables in KEY=VALUE format function Get-EnvironmentVariables { return (Get-ChildItem ENV: | foreach { "WIN_$(Get-LinuxSafeValue -Value ($_.Name -replace '\(|\)','').ToUpper())=$(Convert-ToWSLPath -Path $_.Value)" }) } # converts the C:\foo\bar path to the WSL counter part of /mnt/c/foo/bar function Convert-ToWSLPath { param ( [Parameter(Mandatory=$true)] $Path ) (Get-LinuxSafeValue -Value (($Path -split ';' | foreach { if ($_ -ne $null -and $_ -ne '' -and $_.Length -gt 0) { (( (Fix-Path -Path $_) -replace '(^[A-Za-z])\:(.*)', '/mnt/$1$2') -replace '\\','/') } } ) -join ':')); } function Fix-Path { param ( [Parameter(Mandatory=$true)] $Path ) if ( $Path -match '^[A-Z]\:' ) { return $Path.Substring(0,1).ToLower()+$Path.Substring(1); } else { return $Path } } # Ouputs a string of exports that can be evaluated function Import-EnvironmentVariables { return (Get-EnvironmentVariables | foreach { "export $_;" }) | Out-String } # Just escapes special characters function Get-LinuxSafeValue { param ( [Parameter(Mandatory=$true)] $Value ) process { return $Value -replace "(\s|'|`"|\$|\#|&|!|~|``|\*|\?|\(|\)|\|)",'\$1'; } }
Now in my `.bashrc` I have the following:
#!/usr/bin/env bash source ~/.wsl_helper.bash eval $(winenv)
If I run env
now, I get output like the following:
WIN_ONEDRIVE=/mnt/d/users/rconr/onedrive PATH=~/bin:/foo:/usr/bin WIN_PATH=/mnt/c/windows:/mnt/c/windows/system32
Notice the environment variables that are prefixed with WIN_? These are environment variables directly from Windows. I can now add additional steps to my .bashrc
using these variables.
ln -s "$WIN_ONEDRIVE" ~/OneDrive
Additionally, I added a script to my
~/bin
folder that is in my path called powershell
. This will allow me to make "native" style calls to powershell from within bash
scripts.
#!/usr/bin/env bash # rename to `powershell` # chmod +x powershell . ~/.wsl_helper.bash PS_WORKING_DIR=$(lxssdir) if [ -f "$1" ] && "$1" ~= ".ps1$"; then powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1))) ${*:2}" elif [ -f "$1" ] && "$1" ~!= "\.ps1$"; then powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1))) ${*:2}" else powershell.exe -NoLogo -ExecutionPolicy ByPass ${*:1} fi unset PS_WORKING_DIR
In the powershell
file, you will see a call to source
a file called .wsl_helper.bash
. This script has some helper functions that will do things like transform a path from a Windows style path to a linux WSL path, and do the opposite as well.
#!/usr/bin/env bash # This is the translated path to where the LXSS root directory is export LXSS_ROOT=/mnt/c/Users/$USER/AppData/Local/lxss # translate to linux path from windows path function windir() { echo "$1" | sed -e 's|^\([a-z]\):\(.*\)|/mnt/\L\1\E\2|' -e 's|\\|/|g' } # translate the path back to windows path function wsldir() { echo "$1" | sed -e 's|^/mnt/\([a-z]\)/\(.*\)|\U\1\:\\\E\2|' -e 's|/|\\|g' } # gets the lxss path from windows function lxssdir() { if [ $# -eq 0 ]; then if echo "$PWD" | grep "^/mnt/[a-zA-Z]/" > /dev/null 2>&1; then echo "$PWD"; else echo "$LXSS_ROOT$PWD"; fi else echo "$LXSS_ROOT$1"; fi } function printwinenv() { _winenv --get } # this will load the output exports of the windows envrionment variables function winenv() { _winenv --import } function _winenv() { if [ $# -eq 0 ]; then CMD_VERB="Get" else while test $# -gt 0; do case "$1" in -g|--get) CMD_VERB="Get" shift ;; -i|--import) CMD_VERB="Import" shift ;; *) CMD_VERB="Get" break ;; esac done fi CMD_DIR=$(wsldir "$LXSS_ROOT$HOME/\.env.ps1") echo $(powershell.exe -Command "Import-Module -Name $CMD_DIR; $CMD_VERB-EnvironmentVariables") | sed -e 's|\r|\n|g' -e 's|^[\s\t]*||g'; }