Как искать файлы, где два различных слова существуют?

Felrood от Дуги, форумы Linux предоставили решение и я хотел бы совместно использовать его здесь и закрыть этот вопрос.

Gedit, кажется, отображает данные из stdin в новом "Несохраненном документе". Например:

echo "foobar" | gedit

То, что может быть сделано, является этим:

щелкните правой кнопкой по кнопке Kmenu->, приложения редактирования-> находят gedit там (для меня, который является "утилитами")-> помещенный "gedit, 1$ сохраняет

Для меня, который решил проблему, неважно, использую ли я участника общественной кампании, дельфина, alt+f2 или что-то еще..

14
22.03.2016, 00:42
6 ответов

С инструментами GNU:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

Можно сделать стандартно:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

Но это выполнило бы две власти на файл. Постараться не выполнять это многие greps и все еще быть портативным, все еще позволяя любой символ в именах файлов, Вы могли сделать:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

Причем идея состоит в том, чтобы преобразовать вывод find в формат, подходящий для xargs (который ожидает пробел (SPC/вкладка/NL и другие пробелы от Вашей локали с некоторыми реализациями xargs) разделенный список слов, где единственный, двойные кавычки и обратные косые черты может выйти из пробелов и друг друга).

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

./a
./b

У нас нет способа знать, является ли это одним названным файлом b в названном каталоге a<NL>. или если это - эти два файла a и b.

При помощи .//., потому что // не может появиться иначе в пути к файлу, как произведено find (потому что нет такой вещи как каталог с пустым названием и / не позволяется в имени файла), мы знаем что, если мы видим строку, которая содержит //, затем это - первая строка нового имени файла. Таким образом, мы можем использовать это awk управляйте для выхода из всех символов новой строки, но те, которые предшествуют тем строкам.

Если мы берем пример выше, find произвел бы в первом случае (один файл):

.//a
./b

К которому выходит awk:

.//a\
./b

Так, чтобы xargs рассматривает его как один аргумент. И во втором случае (два файла):

.//a
.//b

Который awk уехал бы, как, таким образом, xargs видит два аргумента.

21
27.01.2020, 19:50
  • 1
    Почему бы не использовать find ... -print0 и grep --null вместо этого? –  razzed 28.06.2017, 18:32
  • 2
    @razzed, не уверенный, что Вы имеете в виду их. grep --null (иначе-Z), используется в первом, но расширение GNU. -print0 (другое расширение GNU), не помог бы здесь. –  Stéphane Chazelas 28.06.2017, 18:56

Если файлы находятся в единственном каталоге, и их имя не содержат пространство, вкладку, новую строку, *, ? ни [ символы и не запускаются с - ни ., это получит список файлов, содержащих ME, затем сузит это к тем, которые также содержат, НАХОДЯТ.

grep -l FIND `grep -l ME *`
8
27.01.2020, 19:50
  • 1
    ЭТОМУ нужно больше upvotes!! Намного более изящный, чем "принятый" ответ. Работавший для меня. –  roblogic 07.11.2016, 05:28

С awk вы также можете запустить:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

Он использует cx и cy для подсчета строк, соответствующих FIND и соответственно ME . В блоке END , если оба счетчика> 0, он печатает FILENAME .
Это будет быстрее / эффективнее с gnu awk :

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +
3
27.01.2020, 19:50

Глядя на принятый ответ, он кажется более сложным, чем должен быть. Версии GNU find, grepи xargsподдерживают строки, заканчивающиеся NULL -. Это так же просто, как:

find. -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

Вы можете изменить команду findдля фильтрации нужных файлов, и она работает с именами файлов, содержащими любой символ; без дополнительной сложности разбора sed. Если вы хотите продолжить обработку файлов, добавьте еще один --nullк последнемуgrep

find. -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

И, как функция:

find_strings() {
    find. -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

Очевидно, используйте принятый ответ, если вы не используете версии этих инструментов GNU.

1
27.01.2020, 19:50

Или используйте egrep -eили grep -Eвот так:

find. -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

или

find. -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

+заставляет найти (, если поддерживается )добавить несколько имен файлов (пути )в качестве аргументов к команде -execed. Это экономит процессы и намного быстрее, чем \;, который вызывает команду один раз для каждого найденного файла.

-type fсоответствует только файлам, чтобы избежать поиска в каталоге.

'(ME.*FIND|FIND.*ME)'— это регулярное выражение, соответствующее любой строке, содержащей «ME», за которой следует «FIND» или «FIND», за которой следует «ME». (одинарные кавычки, чтобы оболочка не интерпретировала специальные символы ).

Добавьте -iк команде grep, чтобы сделать ее нечувствительной к регистру -.

Чтобы найти только те строки, в которых «НАЙТИ» стоит перед «ME», используйте 'FIND.*ME'.

Требуются пробелы (1 или более, но ничего больше )между словами:'FIND +ME'

Разрешить пробелы (0 или более, но ничего другого )между словами:'FIND *ME'

Комбинации с регулярными выражениями бесконечны, и если вас интересует сопоставление только по строке -в -и -временной основе, egrep является очень мощным.

2
27.01.2020, 19:50

TL&DR

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

grep -rlzE '(TermOne.*TermTwo)|(TermTwo.*TermOne)'    # GNU grep

find. -type f -exec grep -q 'TermOne' {} \; \
               -exec grep -q 'TermTwo' {} \; \
               -print

awk '/TermOne/{if(p==0)p=1; if(p==2)p=3}
     /TermTwo/{if(p==0)p=2; if(p==1)p=3}
     p==3{print FILENAME;p=0;nextfile}'./*

Один файл

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

Можно искать два термина с любым чередованием:

grep -E '(TermOne.*TermTwo)|(TermTwo.*TermOne)' file

или просмотр вперед:

grep -P '(?=.*TermOne)(?=.*TermTwo)' file

но только если два термина находятся в одной строке

Также можно сделать так, чтобы весь файл действовал как один файл (, если файл не содержит NUL. Текстовые файлы Unix не )с параметром GNU grep -z:

grep -zE '(TermOne.*TermTwo)|(TermTwo.*TermOne)' file

Невозможно использовать -zс -Pодновременно, поэтому на сегодняшний день невозможны никакие упреждающие решения.

Другой вариант — выполнить grep дважды:

<file grep 'TermOne' | grep -q 'TermTwo'

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

Или, используя awk:

awk '/TermOne/{if(p==0)p=1; if(p==2)p=3}
     /TermTwo/{if(p==0)p=2; if(p==1)p=3}
     p==3{print "both terms found"; exit}' file

список файлов

Первые два приведенных выше решения будут работать для рекурсивного списка всех файлов путем добавления параметров-r(recursive, после чего нет необходимости в имени файла ),-l(список совпадающих имен файлов )и-z(предполагается, что весь файл представляет собой одну строку ).

grep -rlzE '(TermOne.*TermTwo)|(TermTwo.*TermOne)'

Или, используя find (два вызова grep):

find. -type f -exec grep -q 'TermOne' {} \; \
               -exec grep -q 'TermTwo' {} \; \
               -print

Или,при использовании awk (глобус будет включать только PWD):

awk '/TermOne/{if(p==0)p=1; if(p==2)p=3}
     /TermTwo/{if(p==0)p=2; if(p==1)p=3}
     p==3{print FILENAME;p=0;nextfile}'./*
0
20.04.2020, 18:25

Теги

Похожие вопросы