How to make cd arguments case INsensitive?

Enabling cdspell will help:

shopt -s cdspell

From the man page:

cdspell If set, minor errors in the spelling of a directory component in a cd command will be corrected. The errors checked for are transposed characters, a miss- ing character, and one character too many. If a correction is found, the corrected file name is printed, and the command proceeds. This option is only used by interactive shells.


Bash

set completion-ignore-case on in ~/.inputrc (or bind 'set completion-ignore-case on' in ~/.bashrc) would be my recommendation. If you're going to type the full name, why balk at a few presses of the Shift key?

But if you really want it, here's a wrapper around cd that tries for an exact match, and if there is none, looks for a case-insensitive match and performs it if it is unique. It uses the nocaseglob shell option for case-insensitive globbing, and turns the argument into a glob by appending @() (which matches nothing, and requires extglob). The extglob option has to be turned on when defining the function, otherwise bash can't even parse it. This function doesn't support CDPATH.

shopt -s extglob
cd () {
  builtin cd "$@" 2>/dev/null && return
  local options_to_unset=; local -a matches
  [[ :$BASHOPTS: = *:extglob:* ]] || options_to_unset="$options_to_unset extglob"
  [[ :$BASHOPTS: = *:nocaseglob:* ]] || options_to_unset="$options_to_unset nocaseglob"
  [[ :$BASHOPTS: = *:nullglob:* ]] || options_to_unset="$options_to_unset nullglob"
  shopt -s extglob nocaseglob nullglob
  matches=("${!#}"@()/)
  shopt -u $options_to_unset
  case ${#matches[@]} in
    0) # There is no match, even case-insensitively. Let cd display the error message.
      builtin cd "$@";;
    1)
      matches=("$@" "${matches[0]}")
      unset "matches[$(($#-1))]"
      builtin cd "${matches[@]}";;
    *)
      echo "Ambiguous case-insensitive directory match:" >&2
      printf "%s\n" "${matches[@]}" >&2
      return 3;;
  esac
}

Ksh

While I'm at it, here's a similar function for ksh93. The ~(i) modified for case-insensitive matching seems to be incompatible with the / suffix to match directories only (this may be a bug in my release of ksh). So I use a different strategy, to weed out non-directories.

cd () {
  command cd "$@" 2>/dev/null && return
  typeset -a args; typeset previous target; typeset -i count=0
  args=("$@")
  for target in ~(Ni)"${args[$(($#-1))]}"; do
    [[ -d $target ]] || continue
    if ((count==1)); then printf "Ambiguous case-insensitive directory match:\n%s\n" "$previous" >&2; fi
    if ((count)); then echo "$target"; fi
    ((++count))
    previous=$target
  done
  ((count <= 1)) || return 3
  args[$(($#-1))]=$target
  command cd "${args[@]}"
}

Zsh

Finally, here's a zsh version. Again, allowing case-insensitive completion is probably the best option. The following setting falls back to case-insensitive globbing if there is no exact-case match:

zstyle ':completion:*' '' matcher-list 'm:{a-z}={A-Z}'

Remove '' to show all case-insensitive matches even if there is an exact-case match. You can set this from the menu interface of compinstall.

cd () {
  builtin cd "$@" 2>/dev/null && return
  emulate -L zsh
  setopt local_options extended_glob
  local matches
  matches=( (#i)${(P)#}(N/) )
  case $#matches in
    0) # There is no match, even case-insensitively. Try cdpath.
      if ((#cdpath)) &&
         [[ ${(P)#} != (|.|..)/* ]] &&
         matches=( $^cdpath/(#i)${(P)#}(N/) ) &&
         ((#matches==1))
      then
        builtin cd $@[1,-2] $matches[1]
        return
      fi
      # Still nothing. Let cd display the error message.
      builtin cd "$@";;
    1)
      builtin cd $@[1,-2] $matches[1];;
    *)
      print -lr -- "Ambiguous case-insensitive directory match:" $matches >&2
      return 3;;
  esac
}