Каков правильный/лучший способ обработки пробелов и кавычек в завершении bash?Правильная обработка пробелов и котировок в завершении bash
Вот простой пример. У меня есть команда под названием words
(например, программа поиска словаря), которая принимает различные слова в качестве аргументов. Поддерживаемые «слова» может фактически содержать пробелы, и определены в файле с именем words.dat
:
foo
bar one
bar two
Вот мой первый предложил решение:
_find_words()
{
search="$cur"
grep -- "^$search" words.dat
}
_words_complete()
{
local IFS=$'\n'
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "$(_find_words)" -- "$cur"))
}
complete -F _words_complete words
Typing ‘words f<tab>’
правильно завершает команду ‘words foo ’
(с конечное пространство), что приятно, но для ‘words b<tab>’
предлагается ‘words bar ’
. Правильное завершение будет ‘words bar\ ’
. И для ‘words "b<tab>’
и ‘words 'b<tab>’
он не предлагает никаких предложений.
Эта последняя часть, которую я смог решить. Можно использовать eval
для правильного анализа символов (экранированных). Однако, eval
не любит недостающие кавычки, так, чтобы все работало, я должен был изменить search="$cur"
к
search=$(eval echo "$cur" 2>/dev/null ||
eval echo "$cur'" 2>/dev/null ||
eval echo "$cur\"" 2>/dev/null || "")
Это на самом деле работает. И ‘words "b<tab>’
, и ‘words 'b<tab>’
правильно автозаполняют, и если я добавлю ‘o’
и снова нажмите <tab>
, он фактически завершает слово и добавляет правильную закрывающую цитату. Однако, если я попытаюсь заполнить ‘words b<tab>’
или даже ‘words bar\ <tab>’
, он будет автозаполнен до ‘words bar ’
вместо ‘words bar\ ’
, и добавление, например, ‘one’
завершится с ошибкой при запуске программы words
.
Теперь, очевидно, это есть, чтобы справиться с этим правильно. Например, команда ls
может сделать это для файлов namned ‘foo’
‘bar one’
и ‘bar two’
(хотя у него есть проблемы с некоторыми способами выражения имен файлов, когда вы используете (действительную) комбинацию как , '
, так и различные экраны). Однако я не мог понять, как это делает ls
, читая код завершения bash.
Итак, кто-нибудь знает, как правильно справиться с этим? Фактические входные котировки не обязательно сохраняются; Я был бы доволен решением, которое, например, меняет ‘words "b<tab>’
, ‘words 'b<tab>’
и ‘words b<tab>’
на ‘words bar\ ’
(хотя я предпочел бы лишать кавычек, как в этом примере, вместо их добавления).
Спасибо. Это решение работает для оригинальных примеров, но если я добавлю 'rock' n roll 'в words.dat, это не сработает. Мое реальное использование автозаполнения фактически включает в себя слова с апострофами, и именно по этой причине я изначально использовал 'eval'. Это достаточно легко (хотя и не очень элегантно) исправить, добавив дополнительный «поиск и замену» в цикл «for», а затем добавьте еще один цикл для строк, начинающихся с ». Единственная оставшаяся проблема, насколько я вижу, заключается в том, что автозаполнение не продвигает одну позицию курсора, если вы написали целое слово, включая любые закрывающие кавычки. –
Что касается моего комментария выше. Похоже, ситуация немного хуже, чем я думал. Автозаполнение внутри слова, содержащего апострофы (например, попытка автозаполнения «рок-н-ро», написанная с использованием экранированных пространств и апострофа, или одиночные или двойные кавычки) не работает. Причина в том, что переменная 'search' не находится в ее правильной расширенной форме. Некоторые дополнительные замены кажутся возможными, но я не смог заставить это работать правильно для всех трех разных способов экранирования. –
Да, compgen, кажется, зачищает все qoutes ... Что означает, что они должны быть экранированы внутри _find_words – Eugene