How to get the host user home directory in WSL Bash

I want to get the Windows "host" user home directory from within bash, so my theoretical environment variable (or program)

echo $WINHOME

would output something like

/mnt/c/Users/dualed

Would this be somehow possible?

When I search for it, I only find results for the opposite (finding the LXSS user path from the host)

I have a fallback-idea of extracting it from $PATH, as there are some predictable paths like those containing AppData, but I'd prefer to keep it simple, if possible.


With wslpath and wslvar:

$ wslpath "$(wslvar USERPROFILE)"
/mnt/c/Users/felipesantos

You may launch cmd.exe from bash to get the host environment variables. In the following, win_userprofile has your answer, and the other variables are for completeness.

win_userprofile="$(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null)"

win_userprofile_drive="${win_userprofile%%:*}:"
userprofile_mount="$(findmnt --noheadings --first-only --output TARGET "$win_userprofile_drive")"

win_userprofile_dir="${win_userprofile#*:}"

userprofile="${userprofile_mount}${win_userprofile_dir//\\//}"

Sources : Craig Loewen at Microsoft and Michael Hoffman.


Fortunately since Windows 10 build 17063 (included in Windows 10 1803) there is a more straightforward way of sharing environment variables between Windows and WSL - WSLENV.

To make %USERPROFILE% accessible in WSL you list the variable name in the WSLENV variable. If you are not using WSLENV yet then just run the following command once in a cmd.exe session. The command setx permanently writes variables to the master environment in the Windows registry:

setx WSLENV USERPROFILE/up

This WSLENV setting will cause WSL to make %USERPROFILE% from Windows accessible as $USERPROFILE in WSL shell. The Windows directory path will be converted to the Unix format. If you do not want to convert the path, just omit the p:

setx WSLENV USERPROFILE/u

If you need to transfer multiple variables separate them by a colon. More details:

  • https://docs.microsoft.com/en-us/windows/wsl/interop#share-environment-variables-between-windows-and-wsl
  • https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/

I use the variable in my cdw function (cd to Windows path). I define it in ~/.bash_aliases which is executed automatically in Ubuntu:

#!/bin/bash

cdw () {
        if test "$#" -eq 0 ; then
                cd "$USERPROFILE"
        elif test "$1" = - ; then
                cd "$1"
        else
                cd -- "$(wslpath "$@")"
        fi
}

Since my writing of this question, the tool wslpath has been added to WSL/LXSS. This tool can translate windows paths to the corresponding correct (?) mount point on the Linux subsystem, therefore the easiest solution would be now:

export WINHOME=$(wslpath $(cmd.exe /C "echo %USERPROFILE%"))
# echo $WINHOME prints something like /mnt/c/Users/dualed

Also, now it seems possible to cascade environments, so this is now also an option:

export WINHOME=$(cmd.exe /C "cd /D %USERPROFILE% && bash.exe -c pwd")

PS: The wslpath tool seems extremely alpha:

man wslpath
# No manual entry for wslpath
# See 'man 7 undocumented' for help when manual pages are not available.
wslpath /?
# wslpath: /?: Invalid argument
wslpath -?
# wslpath: unrecognized option: ?
# wslpath: Invalid argument
wslpath --help
# wslpath: unrecognized option: -
# wslpath: Invalid argument
wslpath
# wslpath: Invalid argument

just a small warning.


Hopefully helpful update: Currently I'm using a small helper to retrieve windows environment variables

#!/bin/bash
# 'winenv'
cmd.exe /C "echo %$*%" | tr -d '\r'

And using it like:

WINHOME=$(wslpath "$(winenv USERPROFILE)")

This is my reference answer, should the question not be answerable at this time, or be unclear. It does what I wrote in the question I wanted to avoid - guessing the user's home directory by looking for fields containing AppData (and a bit more) within $PATH

#!/bin/bash
IFS=':' read -a fields <<<"$PATH"

for field in "${fields[@]}"; do
        if [[ $field =~ ^(/mnt/.*)/AppData/Local/.* ]]; then
                echo ${BASH_REMATCH[1]}
                exit
        fi
done

It does require of course that there is at least one path in %APPDATA% in Windows, and (more problematic in my opinion) that there is no AppData path set with other user names, e.g. in global scope and finally that the home directory is actually mounted in /mnt.

Example output:

$ winhome
/mnt/c/Users/dualed