Установка и использование Ack, замены Grep, на сервере Ubuntu 14.04

Вступление

Linux- и Unix-подобные системы имеют множество инструментов для поиска текста в файле или каталоге. Один из наиболее распространенных – grep (что значит global regular expression print).

При помощи grep можно без труда найти любой представленный в виде регулярных выражений шаблон в пределах текстового ввода любого объема. Тем не менее, это не самый быстрый инструмент, он был создан как утилита общего назначения без какой-либо оптимизации. Так, для поиска исходного кода был изобретен инструмент по имени ack как альтернатива grep. Он использует регулярные выражения Perl для эффективного поиска исходного кода по шаблону и при этом отфильтровывает нежелательные результаты.

Данное руководство демонстрирует, как использовать ack в качестве мощной альтернативы grep для поиска шаблонов исходного кода. Инструмент ack доступен на любой платформе, которая может использовать Perl, но в данном руководстве используется Ubuntu 14.04.

Установка Ack

Для начала нужно установить инструмент ack на сервер.

На сервере Ubuntu или Debian это так же просто, как установить утилиту из репозитория по умолчанию. Пакет называется ack-grep:

sudo apt-get update
sudo apt-get install ack-grep

Поскольку исполняемый файл также называется ack-grep, можно сказать системе сократить его имя до ack, чтобы использовать его в командной строке, введя следующую команду:

sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep

Теперь инструмент будет отвечать на имя ack вместо ack-grep.

Установка ack на других системах может отличаться. Модуль Perl в CPAN называется App::Ack. На других дистрибутивах Linux имена пакетов данного инструмента могут отличаться.

 На что обращает внимание ack?

Прежде чем приступить к работе с ack, нужно рассмотреть его отличия от grep, а также ознакомиться с файлами, которые находятся в области действия ack.

Инструмент ack был разработан специально для поиска исходного кода программ. Потому ack ищет определенные файлы и игнорирует другие.

К примеру, при поиске структуры каталогов проекта иерархия репозитория системы управления версиями, как правило, не нужна. Она содержит информацию о более старых версиях файла и, скорее всего, приведет к многочисленным повторам. Ack понимает, что здесь искать не нужно, поэтому он игнорирует эти каталоги, благодаря чему результат получается более сжатым и содержит меньше ошибочных данных.
Подобным образом он игнорирует общие резервные файлы, созданные некоторыми текстовыми редакторами. Также нет смысла искать с помощью ack некоторые файлы, которые обычно встречаются в исходных каталогах: сжатые версии веб-файлов, изображения, PDF-файлы и т.д. Все эти функции положительно влияют на результаты почти каждого поиска. Кроме того, эти настройки всегда можно изменить.

Еще одна полезная функция ack состоит в том, что он знает об исходных файлах разных языков. То есть, этот инструмент может найти в структуре каталогов все файлы Python. Это вернет все файлы, которые заканчиваются .ру, а также любой файл, который начинается со строки:

#!/path/to/python

Это выведет определенные по расширению файлы, а также файлы, вызвающие интерпретатор Python с использованием magic number:

#!/path/to/interpreter/to/run

Это надежный способ категоризировать различные файлы как связанные. Кроме того, можно добавлять и изменять группы на свое усмотрение.

Подготовка среды

Лучший способ продемонстрировать работу ack – это использовать его в каталоге исходного кода.

Дерево исходных кодов можно без труда загрузить с открытого сайта вроде GitHub. Установите git, чтобы скачать репозиторий:

sudo apt-get install git

Теперь можно загрузить какой-нибудь проект. Проект neovim – отличный пример, поскольку содержит большое количество различных файлов. Скопируйте репозиторий данного проекта в домашний каталог:

cd ~
git clone https://github.com/neovim/neovim.git

Теперь перейдите в каталог этого проекта:

cd neovim

Чтобы получить представление о проекте и многообразии входящих в него файлов, просмотрите каталог:

ls
BACKERS.md       CMakeLists.txt   Doxyfile   scripts      uncrustify.cfg
clint-files.txt  config           Makefile   src          vim-license.txt
clint.py         contrib          neovim.rb  test
cmake            CONTRIBUTING.md  README.md  third-party

В этом каталоге верхнего уровня находятся файлы .md, простые текстовые файлы, файл Ruby, файл Python. И главная часть проекта написана на языке C.

Чтобы облегчить работу, нужно настроить некоторые функции.

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

echo '--pager=less -RFX' >> ~/.ackrc

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

Простой поиск с ack

Для начала нужно ознакомиться с разницей между объектами поиска grep и ack.

Grep выводит каждый файл каталога, совпавший с шаблоном. Чтобы узнать общее количество файлов в проекте neovim, введите:

find . | wc -l
566

На момент написания статьи проект neovim состоит из 566 файлов. Теперь используйте ack, чтобы сравнить результат и увидеть, сколько файлов из общего их количества будет выведено:

ack -f | wc -l
497

Как видите, ack отбросил около 12% имеющихся файлов.

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

ack restrict
Doxyfile
1851:# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1860:# code bases. Also note that the size of a graph can be further restricted by
1861:# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
vim-license.txt
3:I)  There are no restrictions on distributing unmodified copies of Vim except
5:    unmodified parts of Vim, likewise unrestricted except that they must
. . .

Как видите, ack разделяет найденные экземпляры “restrict”, указывая файл, в котором было обнаружено совпадение. Кроме того, он дает точный номер строки.

Обратите внимание, некоторые из выведенных результатов неточно совпадают с шаблоном: кроме совпадений с “restrict” выведены также вариации вроде “restricted” и “restriction”. Но что если нужно найти только “restrict”?

В таком случае используется шаблон -w, который указывает, что слова должны четко совпадать с границами шаблона. Это позволит устранить вариации слова “restrict”:

ack -w restrict
vim-license.txt
37:       add.  The changes and their license must not restrict others from
clint.py
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.
src/nvim/fileio.c
6846:   * Allow nesting of autocommands, but restrict the depth, because it's
. . .

Как можно видеть, теперь результат показывает только строки, содержащие “restrict”; вариации шаблона были отброшены, и результат имеет компактный вид.

Кроме того, можно заметить, что выведенные строки были обнаружены в файлах различного типа. Так, одна из строк обнаружена в обычном текстовом файле, другая находится в файле Python, также есть несколько совпадений в исходных файлах C.

Чтобы ack показывал только совпадения, обнаруженные в файлах Python, просто введите:

ack -w --python restrict
clint.py
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.

Обратите внимание: не нужно указывать шаблон искомых файлов или использовать специальные регулярные выражения, чтобы получить файлы определенного типа. Ack просто знает файлы многих распространенных языков и может обращаться к ним по имени.

Далее будут рассмотрены способы изменения файлов, выведенных ack для каждого языка, а также определение своих собственных языковых групп.

Анализ поиска

Как видите, результат можно существенно сократить, внеся всего один флаг. Рассмотрим другие способы сокращения результата.

Можно использовать флаг -ch, который значит “как много выведено совпадений?”. Сам флаг -с говорит ack выводить только количество совпадений в каждом файле:

ack -c restrict
Doxyfile:3
Makefile:0
uncrustify.cfg:0
.travis.yml:0
neovim.rb:0
vim-license.txt:5

Этот результат содержит даже файлы, в которых нет совпадений с шаблоном.

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

ack -ch restrict
101

Как видите, таких строк 101. Теперь устраните вариации шаблона, установив четкие границы слова, что значительно сократит результат:

ack -ch -w restrict
16

Конечно, если указать, что нужны только файлы Python, то результат сузится до одного совпадения:

ack -ch -w --python restrict
1

Внося ограничение языка, можно не только сократить результат, но фактически ускорить сам процесс поиска. Дело в том, что ack не просто фильтрует результаты на основе указанного языка – он делает это еще перед поиском, чтобы не искать ненужные файлы.

Это можно проверить, просмотрев сроки поиска при помощи команды time:

time ack -ch restrict
101
real    0m0.407s
user    0m0.363s
sys     0m0.041s

А теперь укажите язык поиска:

time ack -ch -w --python restrict
1
real    0m0.204s
user    0m0.175s
sys     0m0.028s

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

Изменение результатов поиска

Некоторые способы изменения результатов уже были рассмотрены (флаги -c и -h). Тем не менее, существует еще много полезных флагов, которые помогут придать выведенному результату необходимый вид.

К примеру, как было показано ранее, флаг -c показывает количество содержащих совпадение строк в абсолютно каждом файле (0, если совпадений нет). Этот вывод был изменен при помощи флага -h, но в этом случае можно также использовать флаг -l. Он выведет только файлы, в которых найдено совпадение, а также количество совпадений в них:

ack -cl restrict
Doxyfile:3
vim-license.txt:5
clint.py:1
test/unit/formatc.lua:1
src/nvim/main.c:4
src/nvim/ex_cmds.c:5
src/nvim/misc1.c:1
. . .

Как видите, данный результат не содержит файлов, в которых не обнаружено совпадений с шаблоном.

Чтобы узнать столбец, в котором найдено совпадение, ack выведет эту информацию с помощью параметра –column:

ack -w --column --python restrict
clint.py
107:31:      Specify a number 0-5 to restrict errors to certain verbosity levels.

Второе число (после номера строки) – это номер столбца, в котором находится первый символ совпадения. Некоторые текстовые редакторы позволяют перейти по номеру строки и столбца.

К примеру, если открыть файл client.py в редакторе vim, то можно перейти в точное местоположение совпадения, набрав 107G, чтобы перейти в нужную строку, а затем 31|, чтобы перейти в нужный столбец. Такое точное расположение может быть очень полезно, особенно при поиске общей подстроки в пределах больших слов.

Кроме того, ack может отображать строки до или после обнаруженного совпадения. Например, чтобы вывести 5 строк перед совпадением “restrict” в файле python, используйте флаг -B:

ack -w --python -B 5 restrict
102-    output=vs7
103-      By default, the output is formatted to ease emacs parsing.  Visual Studio
104-      compatible output (vs7) may also be used.  Other formats are unsupported.
105-
106-    verbose=#
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.

Указать количество контекстных строк после совпадения можно при помощи флага -А:

ack -w --python -A 2 restrict
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.
108-
109-    filter=-x,+y,...

Можно также задать общий параметр контекста, который будет отображать указанное количество строк до и после совпадения; для этого используется флаг -C. Например, чтобы получить 3 строки контекста в любом направлении, наберите:

ack -w --python -C 3 restrict
104-      compatible output (vs7) may also be used.  Other formats are unsupported.
105-
106-    verbose=#
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.
108-
109-    filter=-x,+y,...
110-      Specify a comma-separated list of category-filters to apply: only

Чтобы вместо самих совпадений вывести только файлы, содержащие совпадения, используйте флаг -f:

ack -f --python
clint.py
contrib/YouCompleteMe/ycm_extra_conf.py

С помощью флага -g можно сделать то же самое, но также указать шаблон структуры файл/каталог. Например, можно найти все файлы языка С, содержащие шаблон “log” в пути, набрав:

ack -g log --cc
src/nvim/log.h
src/nvim/log.c

Работа с типами файлов

Ранее были рассмотрены способы фильтрации файлов по их типу. Чтобы ack показывал только файлы языка С, наберите:

ack -f --cc
test/includes/pre/sys/stat.h
src/nvim/log.h
src/nvim/farsi.h
src/nvim/main.c
src/nvim/ex_cmds.c
src/nvim/os/channel.c
src/nvim/os/server.c
. . .

Чтобы просмотреть список языков, которые знает ack, а также узнать, какие расширения и свойства файлов он связывает с каждой категорией, наберите:

ack --help-types
Usage: ack-grep [OPTION]... PATTERN [FILES OR DIRECTORIES] The following is the list of filetypes supported by ack-grep.  You can
specify a file type with the --type=TYPE format, or the --TYPE
format.  For example, both --type=perl and --perl work.
Note that some extensions may appear in multiple types.  For example,
.pod files are both Perl and Parrot.
--[no]actionscript .as .mxml
--[no]ada          .ada .adb .ads
--[no]asm          .asm .s
--[no]asp          .asp
. . .

Как видите, это показывает это дает параметры соответствия для каждого типа файлов. Можно также сказать ack для исключить файлы определенной категории, предварительно введя “no”.

Таким образом, чтобы просмотреть количество имеющихся файлов языка С, наберите:

ack -f --cc | wc -l
191

Можно выполнить обратную операцию и просмотреть количество файлов, не относящиеся к языку С:

ack -f --nocc | wc -l
306

Как изменить категоризацию типов? К примеру, при поиске файлов CSS нужно отобразить файлы .sass, .scss и .less. Как можно видеть, они уже совпадают в пределах категорий “sass” и “less”, но при необходимости их можно также добавить в категорию CSS. Для этого используется синтаксис:

ack --type-add=TYPE:FILTER:ARGS

Команда –type-add вносит дополнительные правила совпадения для указанного типа (TYPE). В данном случае фильтром (FILTER) является ext (совпадение по расширению файла).

Итак, команда будет иметь следующий вид:

ack --type-add=css:ext:sass,scss,less

Однако текущая команда не выполняет никакого поиска. Чтобы добавить поиск, введите:

ack --type-add=css:ext:sass,scss,less -f --css

Это выведет все файлы с расширением .css, .sass, .scss и .less. Но в данном проекте нет файлов с таким расширением. В любом случае, эта команда не очень полезна, поскольку она существует только для текущей сессии. Чтобы сделать ее постоянной, внесите ее в файл ~/.ackrc:

echo "--type-add=css:ext:sass,less" >> ~/.ackrc

Чтобы создать полностью новый тип, используйте опцию –type-set. Синтаксис имеет точно такой же вид, единственное отличие состоит в том, что он используется для определения несуществующего типа.

Как уже было отмечено, TYPE в базовом синтаксисе означает имя категории, а в качестве фильтра (FILTER) используется расширение файла, но можно использовать и другие фильтры.

Например, фильтр is используется для поиска файла по имени. Так, можно создать TYPE example, который совпадет с файлом example.txt, внеся в файл ~/.ackrc следующее:

--type-set=example:is:example.txt

Также можно определить совпадения с помощью обычных регулярных выражений, используя фильтр match. К примеру, чтобы создать тип bashcnf, совпадающий с файлами .bashrc и .bash_profile, наберите:

echo "--type-set=bashcnf:match:/.bash(rc|_profile)/" >> ~/.ackrc

Итоги

Как видите, ack – очень мощный и гибкий инструмент для работы с исходными кодами, который будет полезен даже при постом поиске файлов в среде Linux.

Tags: , , , , , ,

Добавить комментарий