Я хочу найти файлы, не содержащие определенную строку (в каталоге и ее подкаталогах) и удалить эти файлы. Как я могу это сделать?
Удалять файлы, не содержащие определенную строку
Ответ 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)