Вопрос или проблема
Иногда при доступе к различным директориям случается, что я помню названия или по крайней мере часть названий директории в нашей системе 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:
-
Игнорирование регистра при автодополнении:
В файле
~/.inputrc
добавьте следующую строку:set completion-ignore-case on
Эта настройка сделает автодополнение нечувствительным к регистру, но не изменит поведение команды
cd
напрямую. -
Использование специальной функции:
Ниже приведен фрагмент скрипта, который изменяет стандартное поведение
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:
-
Настройка автодополнения:
Zsh позволяет настраивать автодополнение гораздо глубже, чем Bash. В файле
~/.zshrc
добавьте:zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
Это обеспечит автодополнение независимо от регистра.
-
Использование функции
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
. Эти изменения обеспечат большую гибкость при работе в среде командной строки, особенно когда многочисленные пользователи работают с самыми разными наименованиями директорий.
Теперь с помощью предлагаемых решений вы сможете перемещаться по файловой системе без учета регистра имен директорий, что значительно упростит взаимодействие с системой.