Понимание параметра find (1) -exec (фигурные скобки и знак плюс)

17

Используя следующую команду, не мог бы кто-нибудь объяснить, для чего конкретно нужны конечные фигурные скобки ({}) и знак плюс (+)?

И как команда будет действовать иначе, если они будут исключены из команды?

find . -type d -exec chmod 775 {} +
Райан Прентисс
источник

Ответы:

19

Фигурные скобки будут заменены результатами findкоманды, и chmodони будут запущены для каждого из них. В +марке findпытается работать как несколько команд , как это возможно (так, chmod 775 file1 file2 file3в отличие от chmod 755 file1, chmod 755 file2, chmod 755 file3). Без них команда просто выдает ошибку. Это все объясняется в man find:

-exec command ;

      Выполнить команду ; true, если возвращается 0 статус. Все последующие аргументы для findпринимаются в качестве аргументов команды, пока не ;встретится аргумент, состоящий из ' '. Строка ' {}' заменяется текущим именем файла, обрабатываемым везде, где оно встречается в аргументах команды, а не только в аргументах, где оно одиночно, как в некоторых версиях find. ...

-exec command {} +

      Этот вариант -execдействия запускает указанную команду для выбранных файлов, но командная строка создается путем добавления каждого выбранного имени файла в конце; общее количество вызовов команды будет намного меньше, чем количество совпавших файлов. ...

Тердон
источник
12

В дополнение к ответу Тердона,

  • «Очевидно» -exec …должен заканчиваться точкой с запятой ( ;) или знаком плюс ( +). Запятая специальный символ в оболочке (или, по крайней мере, каждый обстреливать я когда - либо использовал), поэтому, если он будет использоваться как часть findкоманды , он должен быть экранирован или цитируемый ( \;, ";"или ';').
  • При -exec … ;этом {}строка может появляться в команде любое количество раз, включая ноль , два или более, в любой позиции.  Посмотрите это для примера того, почему вы можете обойтись -execбез использования {}.   Наличие двух или более матчей полезно главным образом потому, что в (по крайней мере) некоторые версии find, то {}не нужно быть слово само по себе; у него могут быть другие символы в начале или конце; например,

    find . -type f -exec mv {} {}.bak ";"
    

    С -exec … +, {}строка должна появиться как последний аргумент перед +. Команда как

    find . -name "*.bak" -exec mv {} backup_folder +
    

    приводит к загадочному find: missing argument to ‘-exec’сообщению об ошибке.

    • Обойти это именно специфичном для cpи mvкоманд

      find . -name "*.bak" -exec mv -t backup_folder {} +
      

      или

      find . -name "*.bak" -exec mv --target-directory=backup_folder {} +
      

    {}Должно быть слово само по себе; у него не может быть других символов в начале или конце. И, по крайней мере, в некоторых версиях у findвас может быть не более одной версии {}.

  • Примечание здравомыслия: Вы можете сказать

    находить . -name "* .sh" -type f -executable -exec {} необязательные аргументы здесь ";"

    запустить каждый из ваших скриптов. Но

    находить . -name "* .sh" -type f -executable -exec {} +

    запускает один из ваших скриптов с именами всех остальных в качестве параметров. Это похоже на высказывание

    ./*.sh
    

    как команда оболочки, за исключением того find, что она не гарантирует, что она сортирует свои результаты, поэтому вы не гарантированно работаете aaa.sh (ваш *.shфайл в алфавитном порядке ), как если бы вы работали ./*.sh.

  • Аспект, findкоторый может быть не совсем понятен начинающим, заключается в том, что командная строка, по сути, является исполняемым оператором на тайном языке. Например,

    find . -name "*.sh" -type f -executable -print
    

    средства

    for each file
        if the file’s name matches `*.sh` (i.e., if it ends with `.sh`)
        then
            if it is a plain file (i.e., not a directory)
            then
                if it is executable (i.e., the appropriate `---x--x--x` bit is set)
                then
                    print the file’s name
                end if
            end if
        end if
    end loop
    

    или просто

    for each file
        if the file’s name matches `*.sh`  AND  it is a plain file  AND  it is executable
        then
            print the file’s name
        end if
    end loop
    

    Некоторые -ключевые слова являются одновременно исполняемым действием и тестом. В частности, это верно для -exec … ;; например,

    find . -type f -exec grep -q cat {} ";" -print
    

    переводит на

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

    который напечатает имена всех файлов, содержащих строку « cat». И, хотя это то, что grepможет быть сделано само по себе (с опцией -l(в нижнем регистре L)), может быть полезно использовать его findдля поиска файлов, которые содержат определенную строку И имеют определенный размер И принадлежат определенному владельцу. И были изменены в определенный промежуток времени, ....

    Однако это не работает для -exec … +. Так как -exec … +выполняет одну команду для нескольких файлов, нет смысла использовать ее как логическое условие внутри for each file …цикла.

  • Обратная сторона вышеизложенного заключается в том, что findобычно выход завершается со статусом выхода 0, если вы не укажете ему недопустимые аргументы или он не встретит каталог, который он не может прочитать. Даже если выполняемая вами программа дает сбой (выходит с ненулевым состоянием выхода), она findзавершится со статусом выхода 0.  За исключением случаев,  если программа, с которой вы выполняете -exec … +сбой (выходит с ненулевым состоянием выхода), findвыйдет с ненулевым статусом выхода.

В дополнение к миллиону версий find(1) и тестированию того, что на findсамом деле происходит в паре систем, в выпуске 7 Открытой группы базовых спецификаций, выпуск 2013 года, была представлена ​​некоторая информация о том find, что должно, что можно, а что нельзя.

G-Man говорит: «Восстанови Монику»
источник
3
Помните, что ваш пример использования ... -exec mv {} {}.bak ...не гарантированно работает должным образом со всеми findреализациями. Стандартные состояния POSIX {}должны отображаться в одиночку, чтобы их всегда можно было распознать, в противном случае поведение может оставить символы неизменными или заменить их путем. В первом случае вся ваша команда по существу удалит все файлы, кроме последнего найденного ...
jlliagre