
Я использую zsh
оболочку и внутри нее есть следующий код .zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
Первая строка добавляет путь ~/.zsh/completion
к массиву, хранящемуся внутри переменной среды fpath
.
Вторая строка загружает функцию завершения, называемую _vc
, для пользовательской функции оболочки, называемой vc
. Целью vc
является простое редактирование файла внутри определенной папки ( ~/.cheat
). _vc
определен в файле ~/.zsh/completion/_vc
.
Последние 2 строки включают систему завершения.
Вот код моей функции завершения _vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
Я скопировал это отсюдаадрес, и адаптировал его под свои нужды.
До тех пор, пока в каталоге ~/.cheat
нет файла, имя которого содержит одинарную кавычку, автодополнение работает. Но если такой есть, например foo'bar
, автодополнение завершается с ошибкой:
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
Я нашел решение, заменив двойные кавычки в строке cheatsheets="$(ls ~/.cheat)"
на одинарные cheatsheets='$(ls ~/.cheat)'
.
Теперь, когда я нажимаю Tab
после своей vc
команды, zsh предлагает файлы внутри ~/.cheat
, включая foo\'bar
(похоже, zsh автоматически экранирует одинарную кавычку).
Однако я не понимаю, как и почему это работает. С одинарными кавычками переменная cheatsheets
должна содержать литеральную строку. Поэтому $(...)
подстановка команды не должна быть расширена. Например, если я выполню следующие команды:
myvar='$(ls)'
echo $myvar → outputs `$(ls)` literally
Так почему же '$(ls ~/.cheat)'
внутри развернуто _arguments "1:cheatsheets:(${cheatsheets})" && return 0
? И почему внутри автоматически экранируется одинарная кавычка foo'bar
?
решение1
Если мы настаиваем на том, чтобы делать что-то Неправильным Путем™
#compdef vc
declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0
Фу! Конечно, это сломается, если имя файла содержит символ новой строки, так как (f)
они разделяются.В любом случае парсинг ls
— плохая идея.; ZSH может объединять файлы напрямую в массив:
% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*)
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}
na go'i
go'i
%
Но нам, вероятно, не нужен локальный массив; _files
можно завершить на глобе:
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
Это возвращает полностью определенные пути к файлам; если из каталога поиска требуется только голое имя файла, мы можем вместо этого использовать:
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0