How to make cd arguments case INsensitive?

13,317

Solution 1

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.

Solution 2

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
}
Share:
13,317

Related videos on Youtube

Ankit Vashistha
Author by

Ankit Vashistha

Updated on September 18, 2022

Comments

  • Ankit Vashistha
    Ankit Vashistha over 1 year

    Sometimes while accessing the various directories it happens most of the times that I remember the names or at least part of the names of a directory under our Linux system. But some of the directories are named starting with first character caps or one of the characters in the middle of the name Upper case.

    Can anyone suggest how can I make the arguments following cd command case INSENSITIVE, such that if I perform cd BackupDirectory or cd backupdirectory it could enter the directory name BackupDirectory.

    Of course I don't want to screw the things for other users so if the above is possible, is that possible that the change could be applied just to the session I am using and do not effect other users?

    Ok, I tried set completion-ignore-case on but this just doesn't work. It just helps in that if I type cd b and Tab or Esc Esc it fills the directory name ignoring the case. But, what I need is if I do a cd backupdirectory, it just ignores the case and enters BackupDirectory on its own.

  • jsejcksn
    jsejcksn over 8 years
    This is great. But you will need to add it to inputrc for it to work. Like this: echo "set completion-ignore-case on" >> ~/.inputrc
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 8 years
    @JesseJackson Yes, that was implicit here because the asker already knew, but I'll add it to my answer for future visitors.
  • Nathan Arthur
    Nathan Arthur over 8 years
    Do you have to do anything after running this command for the change to go into effect?
  • Sridhar Sarnobat
    Sridhar Sarnobat over 7 years
    I get the following error when trying to use cd with this: cd:cd:17: no such file or directory: videos (I have a dir called Videos)
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 7 years
    @user7000 Which version, under which shell?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 7 years
    @user7000 Works for me under zsh 5.0.7. Maybe you've set an option that changes the way globbing works? Does it help if you change emulate -L zsh to emulate -LR zsh? (By the way I just fixed a bug, that should have been emulate -L zsh, not emulate zsh, otherwise it would mess up your shell options.)
  • Sridhar Sarnobat
    Sridhar Sarnobat over 7 years
    Thanks for the reply. For some reason now it is working even without me using any of your code. I guess that's good news though ideally I would understand what changed between now and then (maybe it was permissions related which I was continuing to mess with). I'll follow up if I get further issues.
  • sdfsdf
    sdfsdf about 7 years
    I would assume so because simply entering shopt -s cdspell is not doing anything for me
  • math2001
    math2001 almost 7 years
    +1 for bind 'set completion-ignore-case on' in ~/.bashrc
  • Supernormal
    Supernormal over 3 years
    No, it works without anything in addition, at least using bash version 4.3.48