13

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.

3 Answers3

18

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.

dogbane
  • 29,677
16

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
}
  • 1
    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 – jsejcksn Jan 08 '16 at 04:10
  • @JesseJackson Yes, that was implicit here because the asker already knew, but I'll add it to my answer for future visitors. – Gilles 'SO- stop being evil' Jan 08 '16 at 15:34
  • 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) – Sridhar Sarnobat Oct 22 '16 at 19:46
  • @user7000 Which version, under which shell? – Gilles 'SO- stop being evil' Oct 22 '16 at 19:48
  • Ooops, yes I was supposed to say zsh. zsh 5.0.2 (x86_64-pc-linux-gnu) specifically (Ubuntu 14.04). I've been messing with some permissions, which I hope is not relevant. – Sridhar Sarnobat Oct 22 '16 at 22:46
  • 1
    @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.) – Gilles 'SO- stop being evil' Oct 22 '16 at 23:05
  • 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. – Sridhar Sarnobat Oct 23 '16 at 22:36
  • +1 for bind 'set completion-ignore-case on' in ~/.bashrc – math2001 Aug 18 '17 at 07:56
1

For zsh I was able to achieve the desired result by running the command

compctl -M '' 'm:{a-zA-Z}={A-Za-z}'

Upon doing that, I was able to cd into directories without using the same casing as their names.

If you want to be permanent (not just for the session) just add it to your .zprofile in your home directory. That worked for me as well. Hope this helps.

AdminBee
  • 22,803