Graças à ajuda do @steeldriver, consegui descobrir isso! Estranhamente, era um problema com os finais de linha do Windows, embora houvesse apenas uma linha sendo produzida pelo CMD. A solução foi converter a saída do CMD para o estilo Unix e isso resolveu o problema! Aqui está o meu código final:
winpath() {
# get the Windows user path, convert to Unix line endings
user_path=$(echo "$(/mnt/c/Windows/System32/cmd.exe /C echo %HOMEDRIVE%%HOMEPATH%)" | tr -d "\r")
# expand the specified path
target_path=$(readlink -f $1)
# change ~ to $user_path
if grep -q "^/home/" <<< $target_path; then
temp_user_path=$(echo "$user_path" | sed -e 's|\|/|g' -e 's|^\([A-Za-z]\)\:/\(.*\)|/mnt/\L\E/|' -e 's|^M$||')
# if there was something after ~, add it to the end of the $user_path
if grep -q "^/home/\(.*\)/\(.*\)" <<< $target_path; then
target_path=$temp_user_path$(echo "$target_path" | sed -e 's|^/home/*/\(.*\)|/|')
# if there was nothing after ~, $target_path is $user_path
else
target_path=$temp_user_path
fi
fi
# check if a Windows path is getting parsed
if grep -q "^/mnt/[a-z]" <<< $target_path; then
# swap /mnt/[a-z] with [A-Z]: and / with \
echo $(echo "$target_path" | sed -e 's|^\(/mnt/\([a-z]\)\)\(.*\)|\U:\E|' -e 's|/|\|g')
else
# return the user's home directory if a Unix path was parsed
echo $user_path
fi
}