Wiki

Clone wiki

comp-house.repo / abuse-find-exec

Как делать не надо

Некоторые несознательные товарищи склоняют общественность к использованию опции -exec и даже -delete в команде find.

http://linuxaria.com/howto/linux-shell-how-to-use-the-exec-option-in-find-with-examples?lang=en

вот что предлагается:

Some examples with find and exec

find / -name "*.old" -exec /bin/rm {} \;
find / -size +100M -exec /bin/rm {} \;
find . -exec /bin/rm {} \;

Please note, that you should NOT use these examples, In case of deletion of a file GNU find has the option -delete which is safer then “-exec /bin/rm {} ;”. For example:

find / -name "*.old" -delete

In older Unix system you could not have the -delete option, and so you have no choice but to use the -exec action.


find ./ -type f -exec chmod 644 {} \;
find / -user olduser  -type f  -exec chown newuser {} \;
find . -type d -exec chmod 755 {} \;

Все это крайне неполезно по следующим причинам:

  1. Опция exec присутствует не во всех сборках find. Это скорее небольшой недостаток, чем проблема, тем не менее, что есть, то есть, так что не удивляйтесь, если в один прекрасный день сев за чужой компьютер, вы увидите вместо результата ошибку.
  2. Вам придется экранировать все спец-символы в команде. Даже если ничего такого у вас нет, обратите внимание на обратный слеш перед ; в конце каждого примера.
  3. Команда -exec будет исполняться отдельно для каждого аргумента. Удаление тысячи файлов породит тысячу запусков bash, что приводит к ужасающе долгому исполнению команды в целом. Правда, оказывается есть решение, использовать \+ вместо \; . В этом случае find сформирует список и передаст его в команду. Но что будет, если длина этого списока окажется больше, чем максимальная длина командной строки?
  4. Что будет, если в именах обрабатываемых файлов есть пробелы? Правильно, в лучшем случае будет no such file по нескольку раз на каждое такое имя. В худшем, если имена совпадут, будут удалены совершенно посторонние файлы.

Как это делать правильно? Альтернатива?

Использовать в паре с find утилиту хargs.

Первый же пример будет теперь выглядеть вот так:

find / -name "*.old" | xargs rm

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

Все хорошо, но осталась проблема с именами файлов с пробелами. Исправим ее:

find / -name "*.old" -print0 | xargs -0 rm

Все, теперь, когда мы попросили find использовать в качестве разделителя символ NULL, а xargs его принимать, как разделитель, все имена файлов будут переданы и распознаны как надо.

Updated