4

Почему в статьях find всегда используется -exec rm {} \; в тех случаях, когда, насколько я могу судить, более короткий и легкий для ввода и чтения -delete кажется, работает так же хорошо?

Я понимаю, что есть случаи, когда rm с некоторыми опциями, например, -r -f может проделать еще несколько хитростей, но есть множество примеров простого -exec rm {} \; в учебных пособиях по всей сети, но практически нет примеров использования -delete .

Что мне не хватает? Есть ли какая-то причина для этого?

3 ответа3

9

tl; dr: -delete не требуется для POSIX, -exec есть.


факты

Страница справки POSIX 1003.1 для find определяет -exec но не -delete .

Это означает, что -exec должен работать практически везде. Я был бы удивлен, find что есть -delete без -exec . Обратное вполне возможно. Особенно легкие системы, которые используют busybox обычно предоставляют только основные параметры командной строки.

Например, у меня есть OpenWRT на одном из моих маршрутизаторов, и его find понимает -exec , не понимает -delete .

Отсутствие -delete не имеет большого значения, если у вас есть -exec rm … . С другой стороны -delete не может заменить -exec в целом. Это мудрый дизайн, позволяющий сначала опустить -delete .


Личный опыт, наблюдения и мнения

Выше должно быть основной причиной, почему -exec rm {} \; так широко рекомендуется. Вторичным может быть эффект снежного кома. Пользователи читают статьи и примеры, знакомятся с -exec и публикуют свои собственные команды (например, здесь, в Super User). Некоторые из них могут даже не знать, что -delete существует.

Несколько раз я видел (или давал) замечания типа «Вы можете использовать вместо этого -delete ». И ответы были такие: «Спасибо, я этого не знал». Я не помню ни одного ответа «Я знаю, но это не POSIX».


Сказав все это, я склонен упоминать -delete всякий раз, когда -exec rm {} \; появляется. Причина в том, что -delete не создает новый процесс, а -exec rm {} \; вызывает отдельный rm для каждого соответствующего файла. Если вы не можете использовать -delete то ваша следующая мысль должна быть -exec rm {} + которая может удалить несколько файлов одним rm (тем не менее, при необходимости он вызовет rm более одного раза).

Почему -exec … + рекомендуется? Это может быть из-за его ограничений. Я могу представить, как неопытный пользователь думает: «Это работает с rm , позвольте мне использовать его с mv !«Тогда -exec mv {} foo/ + не работает, потому что {} должен быть в конце, непосредственно перед + . Пользователь расстроен и бежит обратно к маме Windows.


Я рекомендую -delete как правило, безопасен здесь на Super User. Большинство вопросов указывают на "большие" ОС, команды find там богаты опциями. И даже если есть пользователь, чья find ограничена, я, вероятно, получу обратную связь. Он или она говорит, что решение не работает для них, и я предлагаю -exec rm … вместо этого, объясните проблему и т.д.

Отдельная статья, которая рекомендует -delete , не получит такой обратной связи. В случае каких-либо проблем пользователь просто перейдет на следующую ссылку, возвращенную Google.

3

Разница в гибкости. Если вы используете -exec, вы выполняете команду для каждого выбранного файла. Если вы используете -exec, то у вас есть возможность применить другие параметры поиска. С -delete вы ограничены в использовании -prune. Кроме того, ваше размещение -delete влияет на ваши результаты. Смотрите фрагмент документации ниже:

-delete
    Delete  files;  true  if removal succeeded.  If the removal failed, 
    an error message is issued.  If -delete fails, find’s exit status will be
        nonzero (when it eventually exits).  Use of -delete automatically turns on 
    the ‘-depth’ option.

    Warnings: Don’t forget that the find command line is evaluated as an 
    expression, so putting -delete first will make find try to delete every-
    thing  below  the  starting  points  you  specified.   When testing a find 
    command line that you later intend to use with -delete, you should
    explicitly specify -depth in order to avoid later surprises.  
    Because -delete implies -depth, you cannot  usefully  use  -prune  and  -delete
    together.

-exec command ;
    Execute  command;  true  if 0 status is returned.  All following arguments 
    to find are taken to be arguments to the command until an argument
    consisting of ‘;’ is encountered.  The string ‘{}’ is replaced by the 
    current file name being processed everywhere it occurs in the arguments
    to  the  command, not just in arguments where it is alone, as in 
    some versions of find.  Both of these constructions might need to be escaped
    (with a ‘\’) or quoted to protect them from expansion by the shell.  
    See the EXAMPLES section for examples of the use of  the  -exec  option.
    The specified command is run once for each matched file.  The 
    command is executed in the starting directory.   There are unavoidable security
    problems surrounding use of the -exec action; you should use the -execdir 
    option instead.

-exec command {} +
    This variant of the -exec action runs the specified command on the 
    selected files, but the command line is built by appending  each  selected
    file name at the end; the total number of invocations of the command 
    will be much less than the number of matched files.  The command line is
    built in much the same way that xargs builds its command lines.  
    Only one instance of ‘{}’ is allowed within the  command.   The  command  is
    executed in the starting directory
0

Более старая "широко рекомендуемая" форма, все еще предпочтительная в системах без -delete , (заменяет эллипсы в зависимости от ситуации):

find ... -print0 ... | xargs -0 /bin/rm ...

Это ограничивает число процессов до двух, вместо того, чтобы позволить -exec порождать потенциально тысячи, но при этом обрабатывать имена файлов со специальными символами правильно (тогда как использование -print0 и пропуск -0 в xargs позволит ошибочно разделить имена файлов).

Он использует еще один процесс, чем find с -delete и имеет некоторые межпроцессные издержки связи, но, как и с -delete значительно быстрее, чем find ... -exec /bin/rm '{}' \' почти во всех случаях.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .