Как сделать аргументы команды cd независимыми от регистра?

Вопрос или проблема

Иногда при доступе к различным директориям случается, что я помню названия или по крайней мере часть названий директории в нашей системе Linux. Но некоторые директории называются с заглавной буквы или с одной из заглавных букв в середине названия.

Может кто-нибудь подсказать, как сделать аргументы команды cd нечувствительными к регистру, так чтобы при выполнении cd BackupDirectory или cd backupdirectory можно было зайти в директорию BackupDirectory?

Конечно, я не хочу портить всё для других пользователей, так что если это возможно, возможно ли применить изменение только в той сессии, в которой я работаю, и не затронуть других пользователей?

Хорошо, я попробовал включить set completion-ignore-case, но это просто не работает. Это лишь помогает, если я ввожу cd b и нажимаю Tab или Esc Esc, то это заполняет название директории, игнорируя регистр. Но что мне нужно, так это чтобы при вводе cd backupdirectory, регистр игнорировался бы и команда входила в BackupDirectory сама.

Включение cdspell поможет:

shopt -s cdspell

Из man страницы:

cdspell Если установлено, небольшие ошибки в написании компонента директории
в команде cd будут исправлены. Проверяются ошибки, связанные со сменой местами букв, пропущенной буквой и одной лишней буквой. Если исправление найдено, исправленное имя файла выводится, и команда продолжается. Эта опция используется только в интерактивных оболочках.

Bash

set completion-ignore-case on в ~/.inputrc (или bind 'set completion-ignore-case on' в ~/.bashrc) было бы моей рекомендацией. Если вы собираетесь вводить полное название, почему бы не нажать несколько раз клавишу Shift?

Но если вы действительно хотите этого, вот обёртка вокруг cd, которая пытается выполнить точное совпадение, и если его нет, ищет нечувствительное к регистру совпадение и выполняет его, если оно уникально. Она использует опцию оболочки nocaseglob для нечувствительного к регистру глобинга и превращает аргумент в шаблон, добавляя @() (который ничего не совпадает и требует extglob). Опция extglob должна быть включена при определении функции, иначе bash не сможет её распознать. Эта функция не поддерживает CDPATH.

shopt -s extglob
cd () {
  builtin cd "$@" 2>/dev/null && return
  local -a options_to_unset options_to_reset matches
  [[ :$BASHOPTS: = *:extglob:* ]] || options_to_unset+=(extglob)
  [[ :$BASHOPTS: = *:nocaseglob:* ]] || options_to_unset+=(nocaseglob)
  [[ :$BASHOPTS: = *:nullglob:* ]] || options_to_unset+=(nullglob)
  [[ :$BASHOPTS: = *:failglob:* ]] && options_to_reset+=(failglob)
  shopt -s extglob nocaseglob nullglob
  shopt -u failglob
  matches=("${!#}"@()/)
  [[ ${#options_to_unset} -eq 0 ]] || shopt -u "${options_to_unset[@]}"
  [[ ${#options_to_reset} -eq 0 ]] || shopt -s "${options_to_reset[@]}"
  case ${#matches[@]} in
    0) # Совпадения нет, даже нечувствительное к регистру. Дать команде cd отобразить сообщение об ошибке.
      builtin cd "$@";;
    1)
      matches=("$@" "${matches[0]}")
      unset "matches[$#-1]"
      builtin cd "${matches[@]}";;
    *)
      echo "Двусмысленное совпадение нечувствительного к регистру имен директории:" >&2
      printf "%s\n" "${matches[@]}" >&2
      return 3;;
  esac
}

Ksh

На всякий случай, вот подобная функция для ksh93. Модификатор ~(i) для нечувствительного к регистру соответствия кажется несовместимым с суффиксом / для совпадения только директории (возможно, это ошибка в моей версии ksh). Поэтому я использую другую стратегию, чтобы исключить недиректории.

function 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 "Двусмысленное совпадение нечувствительного к регистру по директории:\n%s\n" "$previous" >&2; fi
    if ((count)); then print -r -- "$target"; fi
    ((++count))
    previous=$target
  done
  ((count <= 1)) || return 3
  args[$#-1]=$target
  command cd "${args[@]}"
}

Zsh

Наконец, вот версия для zsh. Опять же, разрешение нечувствительного к регистру завершения, вероятно, лучший вариант. Следующая настройка возвращает к нечувствительному к регистру глобингу, если точного совпадения регистров нет:

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

Удалите '', чтобы показать все нечувствительные к регистру совпадения, даже если есть точное совпадение регистров. Вы можете установить это из интерфейса меню compinstall.

cd () {
  builtin cd "$@" 2>/dev/null && return
  emulate -L zsh
  setopt extended_glob
  local matches
  matches=( (#i)$argv[-1](N-/) )
  case $#matches in
    0) # Совпадения нет, даже нечувствительного к регистру. Попробовать cdpath.
      if (($#cdpath)) &&
         [[ $argv[-1] != (|.|..)/* ]] &&
         matches=( $^cdpath/(#i)$argv[-1](N-/) ) &&
         (($#matches==1))
      then
        builtin cd $@[1,-2] $matches[1]
        return
      fi
      # Всё ещё ничего. Дать команде cd отобразить сообщение об ошибке.
      builtin cd "$@";;
    1)
      builtin cd $@[1,-2] $matches[1];;
    *)
      print -lru2 "Двусмысленное совпадение нечувствительного к регистру имен директории:" $matches
      return 3;;
  esac
}

Для zsh мне удалось достичь желаемого результата, запустив команду

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

После этого я смог cd в директории, не используя тот же регистр, что и в их названиях.

Если вы хотите, чтобы это было постоянным (не только для сеанса), просто добавьте это в ваш .zprofile в вашем домашнем каталоге. Это сработало и у меня. Надеюсь, это поможет.

cd `find . -type d | grep -iFx ./case/insensitive/path`

Это, конечно, не сработает, если найдётся несколько совпадений, но это ожидаемо.
Также обратите внимание, что ./ должно быть добавлено, так как grep соответствует всей строке.

Ответ или решение

Чтобы сделать аргументы команды cd нечувствительными к регистру в Linux, необходимо провести некоторые манипуляции в настройках оболочки. Вопрос касается возможности выполнения cd backupdirectory, даже если фактическое название директории имеет другой регистр, например "BackupDirectory", "BACKUPDIRECTORY" и т. д. Рассмотрим проблематику, решения и их реализацию в нескольких популярных оболочках Linux: Bash, Zsh и Ksh.

Теория

Во многих файловых системах Linux чувствительность к регистру является стандартом, что означает, что "BackupDirectory" и "backupdirectory" считаются разными именами. Это позволяет сохранять уникальность имени, однако создает неудобства, когда пользователи не помнят точное написание с учетом регистра.

Оболочки, такие как Bash и Zsh, поддерживают возможности автодополнения и нечувствительного к регистру поиска, которые могут помочь в решении подобных вопросов. Например, в Bash shopt -s nocaseglob и в Zsh настройка опций для автозаполнения через compctl или zstyle.

Пример

Рассмотрим, как сделать путь нечувствительным к регистру для команды cd в разных оболочках Linux. Для этого нам нужно проверить текущие настройки оболочки и при необходимости включить дополнительные опции.

Bash:

  1. Игнорирование регистра при автодополнении:

    В файле ~/.inputrc добавьте следующую строку:

    set completion-ignore-case on

    Эта настройка сделает автодополнение нечувствительным к регистру, но не изменит поведение команды cd напрямую.

  2. Использование специальной функции:

    Ниже приведен фрагмент скрипта, который изменяет стандартное поведение cd, чтобы оно было нечувствительно к регистру:

    shopt -s extglob
    cd() {
     builtin cd "$@" 2>/dev/null && return
     local matches
     shopt -s nocaseglob nullglob
     matches=( "$@"* )
     case ${#matches[@]} in
       0) builtin cd "$@";;
       1) builtin cd "${matches[0]}";;
       *) echo "Несколько совпадений: ${matches[*]}"; return 3;;
     esac
    }

    Этот скрипт пытается выполнить команду cd в обычном режиме, а при отсутствии успеха использует нечувствительное к регистру совпадение.

Zsh:

  1. Настройка автодополнения:

    Zsh позволяет настраивать автодополнение гораздо глубже, чем Bash. В файле ~/.zshrc добавьте:

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

    Это обеспечит автодополнение независимо от регистра.

  2. Использование функции cd:

    Аналогично Bash, в Zsh можно создать функцию, позволяющую выполнять команды cd без учета регистра:

    cd() {
     builtin cd "$@" 2>/dev/null && return
     emulate -L zsh
     setopt extended_glob
     local matches
     matches=( (#i)$argv[-1](N-/) )
     case $#matches in
       0) builtin cd "$@" ;;
       1) builtin cd $@[1,-2] $matches[1] ;;
       *) print -lru2 "Несколько совпадений: $matches"; return 3 ;;
     esac
    }

Ksh:

Ksh также поддерживает подобную функциональность, хотя и не так естественно, как в Bash или Zsh. Пример функции для Ksh:

function 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 print -r -- "$target"; fi
    ((++count))
    previous=$target
  done
  ((count <= 1)) || return 3
  args[$#-1]=$target
  command cd "${args[@]}"
}

Применение

После того как соответствующие функции или настройки добавлены в конфигурационные файлы, они будут работать для текущей сессии. Чтобы изменения были постоянными, следует обновить файлы ~/.bashrc или ~/.zshrc. Эти изменения обеспечат большую гибкость при работе в среде командной строки, особенно когда многочисленные пользователи работают с самыми разными наименованиями директорий.

Теперь с помощью предлагаемых решений вы сможете перемещаться по файловой системе без учета регистра имен директорий, что значительно упростит взаимодействие с системой.

Оцените материал
Добавить комментарий

Капча загружается...