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