Команда оболочки для поиска файлов, содержащих одно слово, но не второе слово

Все

У меня есть два файла ниже в моей машине Linux, и я хотел найти файл, который содержит "word1" и не содержит "word99"

file1.txt
  word1
  word2
  word3
  word4
  word5

file2.txt
  word1
  word2
  word3
  word99

Я использовал приведенную ниже команду для файлов, включающих "word1", но не смог найти никакой информации о том, как ее изменить, чтобы получить имена файлов, содержащие "word1", но не "word99"

find . -name '*.*' -exec grep -r 'word1' {} \; -print > output.txt

Любые указатели будут полезны.

Спасибо Сэнди

3 ответа

    $ grep -lr 'word1' * | xargs grep -L 'word99'
    file1.txt

где:

    -l, --files-with-matches
         Only the names of files containing selected lines are written
         to standard output.
    -R, -r, --recursive
         Recursively search subdirectories listed.
    -L, --files-without-match
         Only the names of files not containing selected lines are written
         to standard output.

В первой части команды перед конвейером получаем:

    $ grep -lr 'word1' * 
    file1.txt
    file2.txt

Приведенная выше команда рекурсивно анализирует файлы внутри подкаталогов и выводит список файлов, содержащих слово word1т.е. file1.txt а также file2.txt,

Позже во второй части | xargs grep -L 'word99'Труба отправляет file1.txt а также file2.txt как вход в xargs который предоставляет им grep в качестве аргументов. grep затем перечисляет файл, который не содержит word99 с использованием -L вариант, т.е. file1.txt,

Нам нужно xargs здесь, так как в первой части команды мы получаем file1.txt а также file2.txt в качестве вывода на стандартный вывод. Нам нужно проанализировать содержимое этих файлов, а не строк file1.txt а также file2.txt,

Следующая команда также дает тот же результат (обратный способ поиска / исключения строк):

      $ grep -Lr 'word99' * | xargs grep -l 'word1'
      file1.txt

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

Чтобы найти файлы, содержащие слово:

grep -iR "слово1" .

-I говорит игнорировать регистр -R является рекурсивным (имеется в виду поиск по подкаталогам). (Заглавная буква задокументирована OpenBSD и больше похожа на ls, поэтому я предпочитаю, чтобы она была больше -r.) Точка указывает, с чего начать поиск.

Чтобы найти имена файлов, содержащие слово:

находить. -имя "слово1"

-Iname является нечувствительной к регистру версией "name".

Период указывает, с чего начать поиск. Текущий каталог часто является хорошим выбором.

Примечание: вы ссылались на "." В одном из ваших примеров. Это было здорово для DOS и, как правило, хорошо для Microsoft Windows, но это действительно плохая привычка для среды Unix. Это заставляет меня думать, что ты знаком с Windows. Хорошо, поймите, что в Windows "НАЙТИ" (или "найти") находит текст в файлах. Unix отличается: "grep" находит текст в файлах, а "find" находит имена файлов.

Теперь, чтобы исключить слово 99 и поместить его в текстовый файл, добавьте следующий текст:

| grep -v word99 >> output.txt

Это ключ трубы, почти всегда Shift-Backslash.

Так, например, если вы хотите сделать оба, используйте:

grep -iR "слово1" . | grep -v word99 >> output.txt
находить. имя "слово1" | grep -v word99 >> output.txt

Часть перед символом канала запускает команду и отправляет вывод в канал в стиле Unix. Затем содержимое отправляется из канала в стандартный ввод следующей команды. grep -v будет смотреть на стандартный ввод, который он получает, и исключать то, что вы хотите. grep -v отправит оставшиеся результаты в стандартный вывод. >> перенаправит стандартный вывод предыдущей команды в конец указанного текстового файла.

Причина, по которой вы не видите документированных опций в команде "find" о том, как исключить текст, заключается в том, что Unix был очень сильно разработан с этой идеей создания более простых программ и использования техники конвейеризации для создания сложных эффектов. В средах Microsoft старый код Microsoft был особенно громоздким с обработкой каналов, поэтому программы в основном пытались включить больше функций в каждую программу. С одной стороны, это кажется более простым для конечного пользователя (имея все встроенное), но этому подходу не хватает согласованности. Когда вы используете Unix, не бойтесь трубопровода: как только вы привыкнете к нему, вы можете обнаружить, что он сильно упрощает вещи, но потому что вы можете использовать свои простые инструменты во многих ситуациях, и поэтому вам не нужно переучивать простые приемы снова и снова (для каждой отдельной программы).

Это находит файлы, которые содержат word1:

$ find . -name '*.*' -type f -exec grep -q 'word1' {} \; -print
./file1.txt
./file2.txt

Это находит файлы, которые содержат word1 но не word99:

$ find . -name '*.*' -type f -exec grep -q 'word1' {} \; '!' -exec grep -q 'word99' {} \; -print 
./file1.txt

Чтобы сохранить вывод в файл:

find . -name '*.*' -type f -exec grep -q 'word1' {} \; '!' -exec grep -q 'word99' {} \; -print >output.txt

Тест -exec grep -q word99 {} \; возвращает True для файлов с word99, Мы ставим ! перед ним, чтобы свести на нет возвращаемое значение. Таким образом, ! -exec grep -q word99 {} \; возвращает True для файлов, которые не имеют word99, ! в одинарных кавычках, потому что, если расширение истории включено, ! может быть активным персонажем

Заметки:

  1. -q опция была добавлена ​​в grep чтобы это было тихо. С -qgrep установит правильный код выхода, но не отображает совпадающие строки в stdout.

  2. -type f тест был добавлен в find так что он возвращает только имена обычных файлов.

Другие вопросы по тегам