Skip to content

Instantly share code, notes, and snippets.

@irsdl
Last active February 13, 2024 21:19
Show Gist options
  • Save irsdl/5fc80ebad0d6fa211d3efdec288250a6 to your computer and use it in GitHub Desktop.
Save irsdl/5fc80ebad0d6fa211d3efdec288250a6 to your computer and use it in GitHub Desktop.
Bash function (`cdw`) to open a Windows style directory in WSL
  • Latest Update The following should be enough as wslpath has already been implemented so we don't need wpath (see comments)
function cdw() {
    cd "$(wslpath "$1")"
}

This function could parse the Windows path, convert it to the WSL path, and then execute cd to that path.

Here's a simple example of a Bash function you can add to your ~/.bashrc or ~/.zshrc file to achieve this. This script checks if the input path is in Windows format and then converts it to the Unix-style path used in WSL:

function wpath() {
    local input_path="${1//\\//}" # Convert backslashes to forward slashes

    # Normalize input to ensure it ends with a '/' if it's a drive letter only
    if [[ "$input_path" =~ ^[a-zA-Z]:$ ]]; then
        input_path="${input_path}/" # Append '/' if input is only 'd:'
    fi

    if [[ "$input_path" =~ ^[a-zA-Z]:/ ]]; then
        # Remove the colon and convert drive letter to lowercase
        local drive="${input_path:0:1}"
        drive=$(echo "$drive" | tr '[:upper:]' '[:lower:]')
        # Remove the drive letter and colon, then prepend with '/mnt/'
        local path_without_drive="${input_path:2}"
        local wslpath="/mnt/$drive$path_without_drive"
        echo "$wslpath" || return
    else
        # If it's not a Windows path, try to cd directly
        echo "$input_path" || return
    fi
}

function cdw() {
    cd "$(wpath "$1")"
}

After updating the function in your .bashrc or .zshrc file, make sure to reload the file:

For Bash: source ~/.bashrc For Zsh: source ~/.zshrc

Try using the cdw command again with a Windows-style path.

Examples:

cdw "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0"

wpath "C:\Program Files (x86)"

file $(wpath 'C:\windows\win.ini')

ls -la $(wpath c:)

ls -la "$(wpath 'C:\Program Files (x86)')"

ChatGPT helped @irsdl to complete this simple task!

@vp777
Copy link

vp777 commented Feb 8, 2024

non-working code for word completion on the path

#problem: COMP_WORDBREAKS includes : and is problematic when trying to replace a word that includes one
#maybe we can just remove : from the global not sure if it will cause any problems
function _cdw() {
  local cur=$(_get_cword :) #gets the current word and ignores a potential : in the COMP_WORDBREAKS
  local trcur=$(wpath "$cur") current_comp  current_drive

  while IFS= read -r -d '' current_comp; do
    #current_comp will be a linux full path, we can turn it to windows path
    #this will be the path that will get fed to the cdw
    #if we dont modify it, we have to modify wpath to accept full paths
    COMPREPLY+=("${current_comp}")
  done < <(compgen -f -d -- "$trcur" | tr '\n' '\0')
}

complete -F _cdw cdw

@irsdl
Copy link
Author

irsdl commented Feb 8, 2024

I was not aware of the wslpath function in WSL which has already been implemented as I was reminded here: https://x.com/shafouzzzzzz/status/1755599338401862024?s=20

As a result, perhaps a better implementation for relying solely on the wslpath function and therefore removing the wpath function will be:

function cdw() {
    cd "$(wslpath "$1")"
}

@vp777
Copy link

vp777 commented Feb 13, 2024

fixed completion routine:
edit: created a new gist here with some updates, eg lsw: https://gist.github.com/vp777/bcb6962fc36bab46bf25b023a9d17e7b

#examples:
#cdw C:\Use[tab]
#cdw C:\Program\ [double tab]
#cdw "C:\Program Files\[double tab]
#
#one small issue with symlinks, eg "cdw C:\Users\All Users\" if you try to complete the path, it will get C:\ProgramData,
#the common prefix with the precompleted and the completed path is C:\ and so your completion is just gonna be C:\ and it wont list files
#we can likely handle this special case by testing whether the $mod_cur is a symlink and changing the current word to point to become that destination path

COMP_WORDBREAKS=${COMP_WORDBREAKS//:} #removes : from word breaks

function cdw() {
    cd "$(wslpath "$1")"
}

function _cdw() {
  local cur mod_cur trcur current_comp  wpath debug=1
  cur=$(_get_cword)
  
  function p() {
      local od=1
      [[ $debug -eq 0 ]] && od=/dev/null
      echo "$@" >&$od
  }
  
  #manually fix some escaped characters, not sure if this the right way, likely there is an alternative to _get_cword that does this job?
  #maybe look into how _cd does this (run `type _cd`)
  #in any case, we remove leading/trailing double quotes and replace "\ " with " "
  mod_cur="$cur"
  mod_cur="${mod_cur#\"}"
  mod_cur="${mod_cur%\"}"
  mod_cur="${mod_cur//\\ / }"
  
  trcur=$(wslpath "$mod_cur") #converts from C:\xxx to /mnt/xxx
  #p "________trcur pre=${trcur}_______"
  #[[ -L "${trcur%\/}" ]] && trcur=$(readlink -f "$trcur") && COMP_WORDS[COMP_CWORD]="${trcur}/" && p yesssssssssssssssss
  #[ "_______trcur post=${trcur}__________"
  
  while IFS= read -r -d '' current_comp; do
    wpath=$(wslpath -w "$current_comp") #converts from /mnt/xxx to C:\xxx
    p "@@@@@ current_comp=${current_comp} @@@@@@@@@ wpath=${wpath} @@@@@@@@@@"
    COMPREPLY+=("\"${wpath}\"")
  done < <(compgen -d -- "$trcur"|tr '\n' '\0')
}

complete -F _cdw cdw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment