Как найти и искать во всех файлах с хэштегом в определенной строке

Я пытаюсь найти способ сканировать папку в моей системе OSX для всех файлов, содержащих определенную строку текста (#SomeTag") в определенной строке (хэштег в первой строке). Просто чтобы уточнить, я ищу текст внутри файла, а не в имени файла.

Я попробовал ag, fzf, а также комбинацию, но не могу заставить ее работать так, как я хочу.

Я хотел бы выполнить поиск в файлах с fzf, в которых есть несколько хэштегов в определенной строке. Например:

#TagOne #TagTwo searchpattern

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

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

 #!/bin/sh
if [ "$#" == 1 ]; then
    ag -Ril $1 ./Evernote | xargs ag --nobreak --nonumbers --noheading . | fzf
fi
if [ "$#" == 2 ]; then
    ag -Ril $1 ./Evernote | xargs ag -il $2 | xargs ag --nobreak --nonumbers --noheading . | fzf
fi
if [ "$#" == 3 ]; then
    ag -Ril $1 ./Evernote | xargs ag -il $2 | xargs ag -il $3 | xargs ag --nobreak --nonumbers --noheading . | fzf
fi

person emKaroly    schedule 25.05.2017    source источник
comment
Что такое файлы с fzf? Как мы можем сказать, что вы ищете только в первой строке? Пожалуйста, подумайте и объясните немного яснее, иначе я боюсь, что никто не сможет вам помочь.   -  person Mark Setchell    schedule 25.05.2017
comment
@MarkSetchell fzf — это нечеткий поиск командной строки.   -  person emKaroly    schedule 25.05.2017


Ответы (2)


for file in `find [path] -type f`; do head -1 $file | grep [pattern] >> /dev/null && echo $file; done

Замените [путь] на каталог, который вы хотите найти, а [шаблон] на то, что вы ищете, например «#TagOne #TagTwo searchpattern».

Бит for file in ``; do ....; done выполняет итерацию по каждой строке, возвращаемой кодом в могилах (вещи с обратной кавычкой), присваивая каждой строке вещь, называемую «файлом». Внутри могил у нас есть find [path] -type f, который находит все «обычные» файлы (за исключением ссылок, каталогов и т. д.) на вашем пути и печатает, отправляет каждый на стандартный вывод (который используется нашим циклом for).

Затем мы вызываем head -1 для каждого из этих файлов, который просто извлекает первую строку из каждого и просматривает ее для вашего шаблона. Поскольку нас не волнует нормальный вывод grep, я перенаправляю его в /dev/null, чтобы он не печатался. Удобно, что код выхода grep может рассматриваться как истина/ложь, в зависимости от того, нашел ли он что-нибудь. && echo $file использует это для печати имени файла, только если grep соответствует чему-то в первой строке.

ОБНОВЛЕНИЕ Для поддержки нескольких шаблонов вы можете связать приведенное выше решение, но в конечном итоге вы будете открывать каждый файл для каждого требуемого шаблона. Если у вас есть много шаблонов для поиска, попробуйте следующее:

for file in `find . -type f`; do                                                                    
  FIRSTLINE=`head -1 $file`;                                                                      
  if [[ $FIRSTLINE == *pattern1* &&                                                                     
        $FIRSTLINE == *pattern2* &&                                                                     
        $FIRSTLINE == *pattern3* ]];
  then
      echo $file;
  fi;
done

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

#!/usr/bin/env python
from os import walk
from os.path import join
import sys

directory = sys.argv[1]  # Use the first argument as the directory to search

for root, subdirs, files in walk(directory):
    for file in files:
        path = join(root, file)
        line = open(path).readline()
        if ('TagOne' in line and      # You could also get these on 
                'TagTwo' in line and  # the command-line...
                'TagThree' in line):
            print path
person Sniggerfardimungus    schedule 25.05.2017
comment
Хорошо, почти работает, проблема в том, что я хочу найти по тегам в любом порядке. Например, #TagOne #TagTwo должен давать тот же результат поиска, что и #TagTwo #TagThree #TagOne. - person emKaroly; 25.05.2017
comment
Вы ищете конкретные хэштеги или просто любые хэштеги? Если вы можете привести конкретные примеры тегов, которые вы ищете (список, который всесторонне определяет все возможности, которые вам нужны), может быть возможно определить регулярное выражение, которое будет им соответствовать. - person Sniggerfardimungus; 25.05.2017
comment
@SniggerfardimungusЯ ищу определенные теги. Например, у меня есть заметки в формате уценки, все файлы содержат одну заметку, и каждая заметка имеет строку Теги: где находятся несколько тегов, таких как C ++, STL и т. Д. Я хочу найти все файлы, в которых присутствуют все искомые теги. - person emKaroly; 25.05.2017
comment
Что ж, ваш код поиска хэштегов намного лучше моего :) Но мне нужно вывести содержимое этих файлов, чтобы fzf мог выполнять нечеткий поиск, что я и делаю с двумя последними командами. После этого я хочу использовать это с vim, например: vim $(fzf). Таким образом, поиск помеченных файлов выводит содержимое, затем я выполняю нечеткий поиск с помощью fzf и, наконец, открываю файл в определенной строке в vim. - person emKaroly; 26.05.2017

Я считаю, что ctags мог бы заполнить счет. Это инструмент, облегчающий навигацию по большим проектам с исходным кодом. Он предоставляет некоторые функции, к которым вы можете привыкнуть в современных IDE, такие как возможность перехода от текущего исходного файла к определениям функций и структур в других файлах. Поскольку ctags по сути является кешированным индексом, вы можете искать любую строку и находить все ее ссылки в вашем проекте/каталоге. Ctags предназначен для работы внутри Vim и Emacs. Примечание: плагин fzf.vim имеет функцию поиска по тегам.

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

--regex-<LANG>=/line_pattern/name_pattern/[flags]
       Define regular expression for locating tags in specific language.

Например, вы можете рекурсивно просканировать все текстовые файлы на наличие #word и сохранить эти результаты в файле тегов. Сначала создайте файл опций ctags:

# ctags definition file for searching for tags. 
--langdef=hashtext
--map-hashtext=+.txt
--regex-hashtext=/.*(#[a-zA-Z0-9]*)/\1/h,hashtag/I   #define your regex here
--fields=+ln

Для примера предположим, что этот файл опций называется hashtext.ctags. Затем запустите ctags в своем каталоге, например:

ctags -R --options=hashtext.ctags *

Это будет рекурсивно искать регулярное выражение и создавать индекс в файле tags. Откройте в vim файлы, которые вы хотите просмотреть по хэштегам, и используйте функцию :tag.

Когда весь этот механизм будет установлен, вы можете использовать fzf для поиска и перехода к различным тегам. Подробности и примеры настроек см. здесь: теги fzf. Или, что еще лучше, используйте плагин fzf vim, в котором есть команды Tag и BTag.

person gregory    schedule 22.09.2018