Как работать с именами файлов, содержащими одинарную кавычку внутри функции автодополнения zsh?

Как работать с именами файлов, содержащими одинарную кавычку внутри функции автодополнения zsh?

Я использую 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

Связанный контент