Как использовать backtick-расширение для заполнения arglist?

11

Из справки :help backtick-expansion:

On Unix and a few other systems you can also use backticks for the file name
argument, for example:
    :next `find . -name ver\\*.c -print`
    :view `ls -t *.patch  \| head -n1`
The backslashes before the star are required to prevent the shell from
expanding "ver*.c" prior to execution of the find program.  The backslash
before the shell pipe symbol "|" prevents Vim from parsing it as command
termination.

Если я наберу команду, взятую из справки, я получу ошибку:

:next `find . -name ver\\*.c -print
E79: Cannot expand wildcards  

Почему пример из справки использует две обратных косых черты вместо одной, и почему это не работает?

Следующая работа:

  • Если я удалю одну из двух обратных косых черт, которые защищают звезду от расширения оболочкой перед findпрограммой:

    :next `find . -name ver\*.c -print`
    
  • Если я удаляю оба обратных слеша и ставлю одинарные кавычки вокруг шаблона ver*.c:

    :next `find . -name 'ver*.c' -print`
    

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

Но помощь дает другой пример:

:view `ls -t *.patch  \| head -n1`

Эта команда работает без каких-либо изменений, не нужно использовать одинарные кавычки, не нужно использовать обратную косую черту.
Я полагаю, что причина этого в том, что lsкоманда (в отличие от -nameаргумента findкоманды) принимает несколько аргументов файла и не видит проблем с расширением оболочки *.patch.

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

find /etc -name '*.conf' | grep input

И это будет работать.

В vim я наберу ту же команду, поставив вокруг нее обратные черты и обратную косую черту перед символом канала, чтобы vim не интерпретировал ее как завершение команды:

:next `find /etc -name '*.conf' \| grep input`

И это работает.

Теперь, если я ввожу ту же команду без канала и grep input, я получаю ошибку:

:next `find /etc -name '*.conf'`
E79: Cannot expand wildcards

Почему в этом случае возникает ошибка, хотя я защищал звезду одинарными кавычками?
И почему сейчас ошибка, а не только раньше с трубкой и grep input?

Чтобы понять, я придумал более простую команду:

find . -name '*.conf'

Он ищет все файлы с расширением .confв рабочем каталоге. Команда работает в оболочке.

Чтобы проверить это в vim, я набрал: :next `find . -name '*.conf'`
И это работает. В этом случае точка обозначает мой текущий рабочий каталог, отображаемый командой Ex, :pwdкоторая является моим домашним каталогом, /home/usernameтак как я запустил с него сеанс vim.

Почему это работает, когда я запрашиваю поиск в текущем рабочем каталоге, тогда как он не работает, когда я запрашиваю поиск в произвольной папке, такой как /etc?

Теперь, если я изменил свой рабочий каталог с /home/usernameна /etcкоманду vim Ex :cd /etcи повторил ту же команду, что и раньше, снова произойдет ошибка:

:next `find . -name '*.conf'`
E79: Cannot expand wildcards

Почему та же команда работает, когда я нахожусь в моей домашней папке, а не когда я нахожусь в /etc?

Я уверен, что есть какая-то логика, но я не могу найти.

Что общее и правильный синтаксис для заполнения списка аргументов с произвольной командой оболочки (содержащей звезду, трубу, поиск в любой директории из любого рабочего каталога)?

Я использую Vim версии 7.4.942 и ЗШ моя оболочка по умолчанию. Я проверил эти команды с минимумом инициализаций ( vim -u NORC -N) из Баша, а также из Zsh.

Нужно ли мне настроить ВИМ называть Баш и не ЗШ?

Сагино
источник
Мне интересно, пытались ли вы запустить vim со всеми этими файлами, переданными в качестве аргументов? Я имею в виду что-то вроде:vim $(find . -name ver*.c)
Влад ГУРДИГА
@VladGURDIGA Я просто пытался и все они работают , как и ожидалось от оболочки: vim $(find . -name ver\*.c -print), vim $(ls -t *.patch | head -n1), vim $(find /etc -name '*.conf' | grep input), vim $(find /etc -name '*.conf'),vim $(find . -name '*.conf')
Сагино
@VladGURDIGA Но я хотел бы знать, как заполнить арглист, не выходя из текущего сеанса, поскольку у меня обычно только один. Я мог бы также перемещаться по оболочке, искать группу файлов и отправлять их на сервер Vim (с аргументом --remoteсм .: vi.stackexchange.com/a/5618/4939 ), но мне любопытно узнать, как сделать это прямо из текущей сессии. Если это невозможно, это нормально, но после прочтения справки кажется, что это можно сделать. Если это так, я хотел бы понять, почему vim так по-разному реагирует на подобные команды.
saginaw
Эй, похоже, что в первом примере команды вы пропускаете закрывающую черту. ;) Кроме этого, я думаю, что пример из справки использует две обратные косые черты вместо одной, чтобы предотвратить расширение оболочки, как объяснено здесь: unix.stackexchange.com/q/104260/23669 .
Влад ГУРДИГА
В случае find /etc -name '*.conf'неработоспособности, я думаю, некоторые забавные имена появляются из этой команды, и это может быть причиной того, почему она сработала, когда прошла по каналу grep.
Влад ГУРДИГА

Ответы:

6

Я до сих пор не знаю, как использовать расширение backtick для заполнения arglist произвольной командой оболочки, однако я нашел обходной путь.

От :help `=:

You can have the backticks expanded as a Vim expression, instead of as an
external command, by putting an equal sign right after the first backtick,
e.g.:
    :e `=tempname()`
The expression can contain just about anything, thus this can also be used to
avoid the special meaning of '"', '|', '%' and '#'.  However, 'wildignore'
does apply like to other wildcards.

Таким образом, вместо непосредственного расширения команды оболочки, например:

:args `shell command`

Мы можем отправить команду оболочки в функцию Vim systemlist()и использовать регистр выражений =для расширения полученного выражения следующим образом:

:args `=systemlist("shell command")`

Чтобы сохранить некоторые нажатия клавиш, я определил следующую команду Ex в моем vimrc ( :PAдля Populate Arglist):

command! -nargs=1 PA args `=systemlist(<q-args>)`

И проверил это с помощью различных команд оболочки:

:PA find . -name ver\*.c -print 2>/dev/null
:PA find . -name 'ver*.c' -print 2>/dev/null
:PA ls -t *.patch  | head -n1
:PA find /etc -name '*.conf' 2>/dev/null | grep input
:PA find /etc -name '*.conf' 2>/dev/null
:PA find . -name '*.conf' 2>/dev/null

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

Сагино
источник