Найти в Дзене
Блокнот математика

Рубрика "Секреты Вим". Файловые маски и подстановки

Привет, коллеги. Обсудим взаимодействие с оболочкой в смысле имен файлов и файловых масок. Всё сказанное относится к unix-подобным системам: под Windows всё тоже можно, но есть своя специфика, в которую я не углубляюсь.

Файловые маски - это что-то вроде упрощенных регулярных выражений, с метасимволами вроде "любой символ" и "любой текст".
Маска, но не файловая
Маска, но не файловая

Когда мы употребляем файловые маски в командной строке Вим, они раскрываются. Можно раскрыть их и самостоятельно, получив имя файла (или имена файлов) с помощью функции expand. Функция эта позволяет расшифровать специальные символы (маски) в именах файлов, среди которых самые распространенные - это * и ?, означающие "любое число любых символов" и "один произвольный символ". Так, *.* означает "любой файл с расширением", *.txt — любой текстовый файл, ?.??? — файл с именем из одной буквы и расширением из трех, и так далее. Помним, что * совпадает и с нулем символов, то есть *italian* совпадет и с my_italian_poesy, и просто с italian.

Менее известна конструкция [abc], которая похожа на символьный класс регулярных выражений и им и является: это любой один символ из перечисленных. Так, *[1-9].dat — файл с данным расширением, у которого имя кончается на цифру.

Самая сложная маска — это **. Большого смысла трактовать это как две маски * подряд, нет. Это означает "что угодно", но применяется рекурсивно к каталогам. До ста каталогов в глубину. Так, **/файл.txt совпадет с данным файлом в текущем каталоге и любом его подкаталоге, включая вложенные. А просто */файл.txt совпадет только с файлом в подкаталоге. Если обычный символ стоит сразу после или непосредственно перед **, то этот текст должен совпасть только в имени каталога верхнего уровня. То есть, /usr/inc**/file.inc будет найден в /usr/include и его подкаталогах, а также в каталоге /usr/inc/ и его подкаталогах, в каталоге /usr/increase и в его подкаталогах, и т.п. Это довольно удобно, на самом деле, если освоить.

Функция expand возвращает строку с именем файла, подпадающим под маску, которую вы указали. Если файлов много, их имена разделены символом новой строки.

Например, expand("*.tex") или expand("**/*.tex").

Можно раскрывать и переменные окружения, например, expand($PATH).

Есть и магия. Если выражение начинается с %, решетки или <, то оно трактуется по-особенному. Символ % означает "текущий файл", решетка означает "другой файл", решетка с номером — другой файл по номеру, и есть еще символы. Вот некоторые из них:

  • <cfile> — имя файла под курсором (может потребовать дополнительной расшифровки, скажем, если там ~/file.txt)
  • <sfile> — имя скрипта или функции
  • <slnum> — номер строки в файле скрипта или функции
  • <cword> или <cWORD> — слово под курсором. Слово в смысле w или W, соответственно.

Можно употреблять модификаторы, с которыми мы уже знакомы:

  • :p — дополнить имя файла до полного пути
  • :h — путь (отцепляется последний элемент пути)
  • :t — файл (оставляется последний элемент пути)
  • :r — отцепляется расширение
  • :e — оставляется только расширение
  • :. — путь относительно текущего каталога (если можно). Например, если файл лежит в /home/math/dissert/pics/scheme.eps, а мы находимся в /home/math/dissert, то получим pics/scheme.eps
  • :~ — то же относительно домашнего каталога.
  • :s?pat?sub? заменяет первое вхождение pat на sub, причем pat — регулярное выражение. Разделитель может быть и другой: любой другой символ, только его не должно быть между.
  • :gs?pat?sub? — то же самое, но заменяет все вхождения.

:S — действует, как shellescape (см. далее).

Например, я открываю файл:

:tabnew ../1.bib

Даю команду :echo expand('%')

Получаю /home/math/1.bib — то же, что даст и %:p

:echo expand('%:h') — /home/math/

:echo expand('%:t') — 1.bib

:echo expand('%:r') — 1

:echo expand('%:e') — bib

Модификаторы можно комбинировать.

А можно же expand('%:r') . '.tex' — поменять расширение.

А можно сделать это проще: expand('%:s?bib?tex?').

Функция escape принимает две строки и позволяет экранировать (снабдить слешем \) все символы, перечисленные во втором аргументе.

Например, escape('***', '*') вернет '\*\*\*'

Это функция общего назначения, а есть более специфичная shellescape, которая готовит строку к использованию в командной строке оболочки.

Она поместит строку в одинарные кавычки и заменит все такие кавычки внутри на '\'' (именно кавычка слеш две кавычки).

Если присутствует второй аргмент и это не нуль и не пустая строка, то экранируются также символы !, % и некоторые другие.

Примеры:

:exe '!ls ' . shellescape(expand('<cfile>:h'), 1)
:call system("chmod +w -- " . shellescape(expand("%")))

Можно избежать прямого вызова shellescape, применив модификатор :S, если модификаторы уместны.

Вернемся к командной строке Вим. Помимо файловых масок, можно применять еще обратные кавычки `...` (кавычка эта обычно слева сверху на клавиатуре, где тильда и буквы Ё). Их содержимое будет выполнено оболочкой, а команда Вим получит ее результат. Примеры из Справки:

:next `find . -name ver\\*.c -print`
:view `ls -t *.patch \| head -n1`

Слеши перед звездочкой нужны, чтобы оболочка не раскрыла маску, а передала ее команде find.

Это применение обратных кавычек стандартно: так же они используются в bash, Перл и не только.

И вы понимаете, что это небезопасно, особенно если вы вызываете что-то не своё.

В Вим есть особенность: если после обратной кавычки стоит знак равенства, то выражение в обратных кавычках вычисляется как выражение Вим.

Это позволяет, в частности, вызывать любые функции, так что можно делать вообще, что хотите:

:tabnew `=MyFileName()`

Функция MyFileName должна вернуть имя файла. Можете, например, запрашивать пароль))

Но это уже другая история...

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022