Reputation: 415
I was thinking of adding something like this to my setup.bash script.
ln -s /mnt/c/Users/Ryan/Downloads $HOME/Downloads
But obviously that isn't always an accurate path so I was hoping to be able to do to something like
ln -s /mnt/c/Users/%USERNAME%/Downloads $HOME/Downloads
or
ln -s %USERPROFILE%/Downloads $HOME/Downloads
I know that obviously Windows % vars wouldn't work in bash but it would be cool if wsl could/does export those vars as $Win32.HOME
or $Win32.USER
or something.
Any thoughts?
Is there any way to do this already?
Upvotes: 22
Views: 21254
Reputation: 406
I found that you can use the WSLENV
interop environment variable (it allows sharing environment variables between Windows/Win32 and WSL).
So in Windows environment variables (I used the GUI but you can do this from CMD/Powershell with setx) I set:
WSLENV
to
Path/ul
/u
says that it's meant for WSL (meaning, share it from Windows to WSL, I believe. I think "u" as in "Unix/Ubuntu")/l
means it is a list of pathsThen in my ~/.zshrc
(since I use zsh; if you use bash then ~/.bashrc or equivalent), I added this:
export WINDOWS_PATH=$Path
path+=($WINDOWS_PATH)
Which makes a WSL env var called WINDOWS_PATH
, then appends the value of that to my WSL PATH
env var (in a kind of weird notation I came across at some point; you can also do PATH=$PATH:$WINDOWS_PATH
).
Keep in mind that Windows is case insensitive and Linux is case sensitive; I exploit this by sharing Path
. If I shared PATH
, then my Linux (WSL) side gets its PATH
variable overwritten, so luckily I could just use Path
and then rename it in WSL to WINDOWS_PATH for readability.
https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
I found out that my VSCode, when connected to WSL, basically shares one process/environment/rc file (presumably the VSCode Server process), or at least the WSLENV changes on the Windows environment variables did not show up til I completely restarted (all open windows of) VSCode. Once I did though, I saw my changes in the VSCode integrated terminal (and the WSLENV part that VSCode adds as well).
I now can see all of my .exe
files in my WSL because of forwarding the Path
environment variable as explained above, so I aliased my 1Password exe op=op.exe
and since op.exe is on the PATH
now, this works great. In fact, because the VSCode server behaves as it does as mentioned above, it seems to integrate with the op.exe
just fine which is now on the PATH
. This means that so far, the 1Password integration with the CLI (on the Windows application), seems to actually work now with WSL.
When I open a WSL terminal, I get a Windows (from 1Password) popup asking to approve/allow the application to login, and I have to enter my password. This is likely not extremely ideal, as I'm sure it's supposed to stay logged in, but I'm just happy that this seems to be working quite well. I tried the Linux 1password op
binary, and I got it working but it wouldn't integrate with the Windows side at all.
Hope this helps!
Upvotes: 1
Reputation: 48794
I added this to my .bashrc
so I can reference my Windows user directory in a similar way to $HOME
:
WIN_HOME=$(wslpath -au "$(cmd.exe /c 'echo %UserProfile%')")
This assigns $WIN_HOME
to a value like /mnt/c/Users/Ryan
. This question discusses wslpath
and other approaches.
You can optionally export
the variable, but that's only needed if you're going to read this variable from subprocesses and not just directly in your shell.
Upvotes: 0
Reputation: 667
In newer versions of WSL2, which I believe have been backported to Win10 1903 and 1909, along with 2004, Microsoft introduced an environment variable WSLENV
that is shared between Win32 and WSL to accomplish exactly what you're after.
You set it equal to a colon-seperated list of variables, together with some syntax to trigger path conversion, and whether the variable should be shared bi-directionally, or whether it should only be shared from Win32-->WSL2 or WSL2-->Win32.
For your specific use-case, on Win32 set WSLENV=USERPROFILE/p
. That will trigger the Win32 env variable %USERPROFILE%
to be passed to WSL (and back to Win32 if you call cmd
or similar once you're in WSL), and to perform path translation.
Now in your setup.bash
script, you can do
ln -s $USERPROFILE/Downloads $HOME/Downloads
For the various syntax of this functionality, see the Microsoft article Share Environment Vars between WSL and Windows.
Upvotes: 7
Reputation: 10984
Remembering that it's a shell's job to evaluate environment variables, and since you can invoke Windows exe's from within Linux on WSL, you can ask a Windows shell (Cmd or PowerShell) to expand out a Windows env-var. Here's how you'd do that with Cmd:
$ cmd.exe /c echo %username%
richturn
$
Alternatively, you can choose to project some of your Windows environment variables into WSL if you prefer :)
Upvotes: 20
Reputation: 325
rather old, but I find this very useful.
this changes dirs to your windows home directory:
cd `cmd.exe /c echo %systemdrive%%homepath% 2> /dev/null | tr -d '\r' | xargs -0 wslpath`
Upvotes: 0
Reputation: 570
Reviving this old post with this simple bash function I decided to add to my /etc/bash.bashrc
winenv()
{
if [[ "$#" -eq 0 || "$1" == "--help" ]]
then
echo $'\n'"Usage:"
echo $'\t'"winenv [-d] WINDOWS_ENVIRONEMENT_VARIABLE_NAME"
echo $'\t'"-d: Defines environment variable in current shell"
echo $'\t'" Note that paths will be translated into un*x-like paths"$'\n'
return
fi
local IFS='$\n'
local PATH_TO_TRANSLATE=$1
[[ "$1" == "-d" ]] && PATH_TO_TRANSLATE=$2
local VAR=$(2>/dev/null cmd.exe /c echo %${PATH_TO_TRANSLATE}% | tr -d '\r')
local NEW=$(wslpath -u "${VAR}" 2>/dev/null || echo ${VAR})
echo "${PATH_TO_TRANSLATE} = $(printf '%q' "${VAR}") -> ${NEW}"
[[ "$1" == "-d" ]] && export "${PATH_TO_TRANSLATE}=${NEW}"
}
for EnvVar in 'USERNAME' 'USERPROFILE' 'USERDOMAIN' 'USERDNSDOMAIN' 'WINDIR' 'SystemDrive' 'SystemRoot' 'TNS_ADMIN' 'ORACLE_HOME' 'CLIENT_NAME' 'HOMEDRIVE' 'HOMEPATH' 'TMP' 'TEMP'
do
winenv -d $EnvVar >/dev/null
done
Upvotes: 2
Reputation: 6900
Here is what I did:
Since WSL now has interop between windows and WSL, I took advantage of that.
I have a powershell script in my ~/
folder called ~/.env.ps1
# 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 that I have that, in my .bashrc
I have something like the following:
function winenv() {
echo $(powershell.exe -Command "Import-Module .\.env.ps1; Import-EnvironmentVariables") | sed -e 's|\r|\n|g' -e 's|^[\s\t]*||g';
}
eval $(winenv)
A caveat to this, that I have found, is that I had to put the full path to .env.ps1
in that command. What I did for that was wrote a function that converts the wsl style path back to the windows path. Then used that to translate it.
CMD_DIR=$(wsldir "/mnt/c/Users/$USER/AppData/Local/lxss$HOME/\.env.ps1")
because this is the function that loads the environment variables, I have to hard code the full path, to some extent. I did end up actually setting an environment variable in bash called LXSS_ROOT=/mnt/c/Users/$USER/AppData/Local/lxss
and then used that.
Then, when I start a new shell, and I run env
I get the following:
WIN_ONEDRIVE=/mnt/d/users/rconr/onedrive
PATH=~/bin:/foo:/usr/bin
WIN_PATH=/mnt/c/windows:/mnt/c/windows/system32
and I can now add something like ln -s "$WIN_ONEDRIVE" "~/OneDrive"
to my .bashrc
For your example, you would do this:
ln -s $WIN_USERPROFILE/Downloads $HOME/Downloads
Additionally, I then created a script in my bin path called powershell
.
# 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
}
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
So I can then do something like this:
$ powershell ~/my-ps-script.ps1
$ powershell -Command "Write-Host 'Hello World'"
I am sure there are improvements that can be made to all of this. But, it is currently working for my scenario.
Here is a gist of the scripts I use.
Upvotes: 4