Удалять файлы, не содержащие определенную строку

Я хочу найти файлы, не содержащие определенную строку (в каталоге и ее подкаталогах) и удалить эти файлы. Как я могу это сделать?

Ответ 1

Следующее будет работать:

find . -type f -print0 | xargs --null grep -Z -L 'my string' | xargs --null rm

Это сначала использует find для печати имен всех файлов в текущем каталоге и любых подкаталогах. Эти имена печатаются с нулевым терминатором, а не с обычным разделителем строк (попробуйте подключить вывод к od -c, чтобы увидеть эффект аргумента -print0.

Затем параметр --null в xargs сообщает ему принимать входы с нулевым завершением. xargs затем вызовет grep в списке имен файлов.

Аргумент -Z для grep работает как -print0 аргумент find, поэтому grep будет распечатывать его результаты с нулевым завершением (поэтому для окончательного вызова xargs требуется --null вариант тоже). Аргумент -L для grep заставляет grep печатать имена файлов этих файлов в его командной строке (добавлен xargs), которые не соответствуют соответствуют регулярному выражению: p >

моя строка

Если вам нужно простое совпадение без магии регулярного выражения, добавьте параметр -F. Если вам нужны более мощные регулярные выражения, дайте аргумент -E. Это хорошая привычка использовать одинарные кавычки, а не двойные кавычки, так как это защищает вас от любой магии оболочки, применяемой к строке (например, подстановки переменных).

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

Проблема с вызовом grep непосредственно из команды find с аргументом -exec заключается в том, что grep затем запускается один раз для каждого файла, а не один раз для цельной партии файлов, как это делает xargs. Это намного быстрее, если у вас много файлов. Также не поддавайтесь соблазну делать такие вещи, как:

rm $(some command that produces lots of filenames)

Всегда лучше передать его на xargs, поскольку он знает максимальные пределы командной строки и будет вызывать rm несколько раз каждый раз с таким количеством аргументов, сколько он может.

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

В качестве альтернативы

grep -r -L -Z 'my string' . | xargs --null rm

тоже будет работать (и короче). Аргумент -r для grep заставляет его читать все файлы в каталоге и рекурсивно спускаться в любые подкаталоги). Используйте подход find ..., если вы хотите также выполнить некоторые другие тесты в файлах (например, возраст или разрешения).

Обратите внимание, что любой из однобуквенных аргументов с одним указателем штриховки может быть сгруппирован (например, как -rLZ). Но обратите внимание также, что find не использует те же соглашения и имеет многобуквенные аргументы, введенные с одной тире. Это по историческим причинам и никогда не было исправлено, потому что это сломало бы слишком много скриптов.

Ответ 2

GNU grep и bash.

grep -rLZ "$str" . | while IFS= read -rd '' x; do rm "$x"; done

Используйте решение find, если требуется переносимость. Это немного быстрее.

Ответ 3

Я могу придумать несколько способов приблизиться к этому. Здесь один: find и grep для создания списка файлов без соответствия, а затем xargs rm them.

find yourdir -type f -exec grep -F -L 'yourstring' '{}' + | xargs -d '\n' rm

Это предполагает, что инструменты GNU (grep -L и xargs -d не переносятся) и, конечно же, не содержат имен файлов с новыми символами. Преимущество состоит в том, что один раз за один файл не выполнялся grep и rm, поэтому он будет достаточно быстрым. Я рекомендую тестировать его с помощью "эха" вместо "rm", чтобы убедиться, что он выбирает правильные файлы, прежде чем развязать уничтожение.

Ответ 4

Одна из возможностей -

find . -type f '!' -exec grep -q "my string" {} \; -exec echo rm {} \;

Вы можете удалить echo, если этот предварительный просмотр выглядит правильно.

Ответ 5

EDIT: Вот как вы НЕ ДОЛЖНЫ делать это! Причина дается здесь. Спасибо @ormaaj за указание на это!

find . -type f | grep -v "exclude string" | xargs rm

Примечание: шаблон grep будет соответствовать полному пути к файлу из текущего каталога (см. вывод find . -type f)