Следующая новость
Предыдущая новость

Конкурс хаков: делаем рекурсивный поиск по файлам в каталогах при помощи grep и регулярок

19.09.2017 12:56
Конкурс хаков: делаем рекурсивный поиск по файлам в каталогах при помощи grep и регулярок

Содержание статьи

  • Поздравляем участника конкурса
  • Подключаем к делу регулярки
  • Конкурс продолжается

Для рекурсивного поиска по файлам в директории удобно использовать grep. Многие с ним, как и с его аналогами (ack, rg, ag, pt, git grep…), хорошо знакомы. Вывод grep можно хорошо сопрячь с открытием нужного файла в текстовом редакторе — сразу с найденной позицией. Например, Vim поддерживает открытие файла на нужной строчке c аргументом +ln, где ln — это номер строки; KWrite имеет опцию --line ln; в других редакторах почти наверняка есть соответствующие опции.

Поздравляем участника конкурса

Этот текст был прислан на конкурс авторов, который мы запустили весной. Мы разобрались с большим количеством пришедших материалов, подвели итоги и наградили победителей. Автор этой заметки получил приз — трехмесячную подписку на «Хакер». Поздравляем!

Запуская grep в режиме рекурсивного поиска, мы указываем ему опцию вывода номера строки -n. Пример поиска на коде ядра:

$ grep -nre 'struct task_struct' scsi/scsi_host.h:562: struct task_struct * ehandler; scsi/libfc.h:458: struct task_struct *resp_task; scsi/libfcoe.h:335: struct task_struct *kthread; asm-generic/switch_to.h:23: struct task_struct *); asm-generic/mmu_context.h:11:struct task_struct; ...

В выводе у нас будет все, что нужно для запуска редактора. Если добавить следующий код в свой .bashrc, мы получим команду, которая будет запускать редактор на каждой найденной строчке:

kgrep() { IFS=$'n'; for i in $(grep -nrPe $1 ${2:-.}) do kwrite $(echo "$i" | cut -d ':' -f 1) --line $(echo "$i" | cut -d ':' -f 2) done }  open_vim() { vim $(echo "$1" | cut -d ':' -f 1) +$(echo "$1" | cut -d ':' -f 2) }  vgrep() { IFS=$'n'; for i in $(grep -nrPe $1 ${2:-.}) do open_vim "$i" done }

Пример запуска:

$ kgrep 'struct task_struct {' $ vgrep 'struct inode {'

Неудобство состоит в том, что редактор будет запускаться последовательно для всех найденных строчках всех файлов. Иногда бывает уместнее сначала выбрать какой-то результат поиска, а уже потом открывать его в редакторе. Это очень легко делается с помощью консольной программы dialog. Для этого необходимо в свой .bashrc добавить следующую функцию:

vvgrep() { declare -a args=()  while read do tag=$(echo "$REPLY" | cut -d ':' -f 1-2) item=$(echo "$REPLY" | cut -d ':' -f 3-) args+=("$tag" "$item") done < <( grep --color=no -nrPe $1 ${2:-.} )  exec 3>&1 result=$(dialog --menu "Please select the file" 0 0 0 "${args[@]}" 2>&1 1>&3) exitcode=$? [[ $exitcode -eq 0 ]] && open_vim "$result"  while [[ $exitcode -eq 0 ]] do result=$(dialog --default-item "$result" --menu "Please select the file" 0 0 0 "${args[@]}" 2>&1 1>&3) exitcode=$? [[ $exitcode -eq 0 ]] && open_vim "$result" done exec 3>&- clear }

Теперь после поиска у нас будет выводиться диалоговое меню с результатами в виде отдельных строк. Можно выбрать одну из них и открыть в редакторе. Последний пример приведен для редактора Vim, для остальных делается по аналогии.

Подключаем к делу регулярки

Grep поддерживает регулярные выражения Perl, а это значит, что можно писать продвинутые запросы для рекурсивного поиска. Например, поиск определения структуры:

$ grep -nrPe 'structs+task_structs*{' linux/sched.h:483:struct task_struct {

Или макроса:

$ grep -nrPe '#s*defines+PAGE_SIZE' asm-generic/page.h:17:#define PAGE_SIZE (1 << PAGE_SHIFT) asm-generic/page.h:19:#define PAGE_SIZE (1UL << PAGE_SHIFT) uapi/linux/a.out.h:128:#define PAGE_SIZE 0x400 linux/raid/pq.h:49:# define PAGE_SIZE 4096

Или же вывести определение typedef:

$ grep -Pzore 'typedefs+structs+{[^}]++}s*atomic_ts*;' linux/types.h:typedef struct { int counter; } atomic_t;

Или функции:

$ grep -Pzore 'batomic_incs*([^)]+)s*{[^}]+}' asm-generic/atomic.h:atomic_inc(atomic_t *v) { atomic_add_return(1, v); }

В последних двух случаях используется опция -z для того, чтобы шаблон сопоставлялся по нескольким строкам. Эта опция объединяет все строки в одну. Также в последних случаях хорошо использовать рекурсивный шаблон сопоставления скобочек, чтобы включать вложенные.

Понятное дело, что подобные запросы не будешь каждый раз набирать в консоли. Поэтому их удобно обернуть в функции bash и добавить в свой .bashrc.

cstruct() { grep --include='*.[ch]' -nrPe '(?m)structs+'$1's*{' "${2:-.}" }  cdefine() { grep --include='*.[ch]' -nrPe '#s*defines+'$1 "${2:-.}" }  ctypedef() { grep --include='*.[ch]' -zorPe 'typedefs+((?=struct)?((?s*w+)?s*(?{(?:(?>[^{}]+)|(?&sbody))*}))|(?:w+s+)+)s*'$1's*;' "${2:-.}" }  cfunc() { grep --include='*.[ch]' -Pzore 'w[ws*]+s*b'$1's*(?((?:(?>[^()]+)|(?&fargs))+))s*(?{(?:(?>[^{}]+)|(?&sbody))*})' "${2:-.}" | tr '' 'n' }
Конкурс хаков: делаем рекурсивный поиск по файлам в каталогах при помощи grep и регулярок

В таком случае весь запрос сводится к вызову соответствующей команды, где первый аргумент — шаблон, а последующие — файлы и папки для поиска.

Конкурс продолжается

Мы решили продлить конкурс и превратить его в постоянную акцию. Прислав нам описание хака, полезный совет или описание клевой неизвестной проги, ты по-прежнему можешь получить подписку на месяц, три месяца или, если постараешься, на год. Следуй рекомендациям и присылай свой текст!

Источник

Последние новости