Розуміння параметра -exec `find '


53

Я можу постійно шукати синтаксис

find . -name "FILENAME"  -exec rm {} \;

головним чином тому, що я не бачу, як саме -execпрацює деталь. Яке значення дужок, зворотної косої риски та крапки з комою? Чи є інші випадки використання цього синтаксису?


11
@Philippos: Я бачу вашу думку. Будь ласка, майте на увазі, що довідкові сторінки є довідковими, тобто корисними для тих, хто розуміє питання, щоб шукати синтаксис. Для когось, хто є новим у цій темі, вони часто є виразними та формальними, щоб бути корисними. Ви побачите, що прийнята відповідь приблизно в 10 разів довша за вказівку на чоловічій сторінці, і це не просто так.
Zsolt Szilagy

6
Навіть стара версія POSIX manчитає ім'я утиліти_коригування або аргумент, що містить лише два символи "{}", буде замінено на поточне ім'я шляху , яке мені здається недостатнім. Крім того, він має приклад -exec rm {} \;, як і у вашому питанні. У мої часи ледь не було жодних інших ресурсів, окрім «великої сірої стіни», книг друкованих manсторінок (папір хоч і зберігання). Тож я знаю, що цього достатньо для когось нового в темі. Ваше останнє питання, правда, справедливо задати тут. На жаль, ні у @Kusalananda, ні у мене немає відповіді на це.
Філіппос

1
Comeon @Philippos. Ви справді говорите Кусалананді, що він не покращився після створення сторінки? :-)
Zsolt Szilagy

1
@allo Хоча xargsіноді зручно, findможе передавати безліч аргументів шляху, щоб командувати без нього. -exec command... {} ++замість \;) проходить стільки шляхів після, command...скільки підходить (у кожної ОС є власний ліміт на тривалість командного рядка). І як xargs, то +-завершённий форма find«и -execдії також буде працювати command...кілька разів в рідкісних випадках, коли є занадто багато шляхів , щоб поміститися в межах.
Елія Каган

2
@ZsoltSzilagy я цього не сказав і не мав на увазі. Він тебе дуже годував, я просто думаю, що ти досить доросла, щоб їсти самостійно. (-;
Філіппос

Відповіді:


90

Ця відповідь наводиться в наступних частинах:

  • Основне використання -exec
  • Використання -execв поєднанні зsh -c
  • Використання -exec ... {} +
  • Використання -execdir

Основне використання -exec

-execОпція має зовнішню утиліту з додатковими аргументами в якості аргументу і виконує його.

Якщо рядок {}присутній де-небудь у даній команді, кожен її екземпляр буде замінений на ім'я контуру, який зараз обробляється (наприклад ./some/path/FILENAME). У більшості оболонок два символи {}не потрібно цитувати.

Команда повинна бути припинена з ;форою , findщоб знати , де вона закінчується (як може бути додатковими варіантами згодом). Для захисту ;від оболонки, вона повинна бути вказана в якості \;або ';', в іншому випадку оболонка буде бачити це як кінець findкоманди.

Приклад ( \наприкінці перших двох рядків - лише для продовження рядків):

find . -type f -name '*.txt'      \
   -exec grep -q 'hello' {} ';'   \
   -exec cat {} ';'

Тут ви знайдете всі звичайні файли ( -type f), імена яких відповідають шаблону *.txtв поточному каталозі або нижче. Потім він перевірить, чи helloвідбувається рядок у будь-якому з знайдених файлів із використанням grep -q(який не дає жодного результату, а лише статус виходу). Для тих файлів, які містять рядок, catбуде виконано виведення вмісту файлу в термінал.

Кожен -execтакож діє як "тест" на імена шляхів, знайдені так findсамо, як -typeі -nameробить. Якщо команда повертає нульовий статус виходу (означає "успіх"), наступна частина findкоманди вважається, інакше findкоманда продовжується з наступним іменем шляху. Це використовується у наведеному вище прикладі для пошуку файлів, що містять рядок hello, але для ігнорування всіх інших файлів.

Наведений вище приклад ілюструє два найпоширеніші випадки використання -exec:

  1. Як тест для подальшого обмеження пошуку.
  2. Виконувати якусь дію над знайденою назвою шляху (зазвичай, але не обов'язково в кінці findкоманди).

Використання -execв поєднанні зsh -c

Команда, яку -execможна виконати, обмежується зовнішньою утилітою з необов'язковими аргументами. Використовувати вбудовані оболонки, функції, умовні умови, трубопроводи, перенаправлення тощо безпосередньо безпосередньо -execнеможливо, якщо тільки не загорнутий у щось на зразок sh -cдочірньої оболонки.

Якщо bashпотрібні функції, використовуйте bash -cзамість sh -c.

sh -cзапускається /bin/shзі скриптом, заданим у командному рядку, після чого необов'язкові аргументи командного рядка до цього сценарію.

Простий приклад використання sh -cсамостійно, без find:

sh -c 'echo  "You gave me $1, thanks!"' sh "apples"

Це передає два аргументи до дочірнього сценарію оболонки:

  1. Рядок sh. Це буде доступно як $0всередині скрипту, і якщо внутрішня оболонка видає повідомлення про помилку, вона додасть її до цього рядка.

  2. Аргумент applesдоступний як $1в сценарії, і якщо б там було більше аргументів, то вони були б доступні $2, і $3т.д. Вони також будуть доступні в списку "$@"(за винятком , $0яка не буде частиною "$@").

Це корисно в поєднанні з тим -exec, що дозволяє нам робити довільно складні сценарії, які діють на імена шляхів, знайдені find.

Приклад: Знайдіть усі звичайні файли, які мають певний суфікс імені файлу, і змініть цей суфікс імені файлу на інший суфікс, де суфікси зберігаються у змінних:

from=text  #  Find files that have names like something.text
to=txt     #  Change the .text suffix to .txt

find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'

Всередині внутрішнього скрипту $1буде рядок text, $2він буде рядком txtі $3буде будь-яким способом, findякий знайде для нас ім'я шляху . Розширення параметра ${3%.$1}прийме ім'я шляху та видалить із нього суфікс .text.

Або, використовуючи dirname/ basename:

find . -type f -name "*.$from" -exec sh -c '
    mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'

або із доданими змінними у внутрішньому сценарії:

find . -type f -name "*.$from" -exec sh -c '
    from=$1; to=$2; pathname=$3
    mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'

Зауважте, що в останньому варіанті змінні fromта toдочірня оболонка відрізняються від змінних з однаковими іменами у зовнішньому скрипті.

Сказане вище - правильний спосіб виклику довільного складного сценарію з -execдопомогою find. Використання findв циклі, як

for pathname in $( find ... ); do

є схильною до помилок і неелегантною (особиста думка). Він розділяє назви файлів на пробіли, викликає глобальну назву файлів, а також змушує оболонку розширювати повний результат, findперш ніж навіть запустити першу ітерацію циклу.

Дивитися також:


Використання -exec ... {} +

;В кінці може бути замінений +. Це змушує findвиконати дану команду якомога більше аргументів (знайдених імен шляхів), а не один раз для кожного знайденого імені шляху. Рядок {} повинен відбуватися безпосередньо перед тим, +щоб це спрацювало .

find . -type f -name '*.txt' \
   -exec grep -q 'hello' {} ';' \
   -exec cat {} +

Тут findбуде зібрано отримані імена шляхів та виконано catна якомога більше з них одночасно.

find . -type f -name "*.txt" \
   -exec grep -q "hello" {} ';' \
   -exec mv -t /tmp/files_with_hello/ {} +

Так само і тут, mvбуде виконуватися якомога менше разів. Останній приклад вимагає GNU mvвід coreutils (який підтримує -tопцію).

Використання -exec sh -c ... {} +також є ефективним способом перебирання набору імен шляхів з довільно складним сценарієм.

Основи такі ж, як і під час використання -exec sh -c ... {} ';', але тепер сценарій містить набагато довший список аргументів. Їх можна перекинути, "$@"переглянувши всередині сценарію.

Наш приклад з останнього розділу, який змінює суфікси назви файлів:

from=text  #  Find files that have names like something.text
to=txt     #  Change the .text suffix to .txt

find . -type f -name "*.$from" -exec sh -c '
    from=$1; to=$2
    shift 2  # remove the first two arguments from the list
             # because in this case these are *not* pathnames
             # given to us by find
    for pathname do  # or:  for pathname in "$@"; do
        mv "$pathname" "${pathname%.$from}.$to"
    done' sh "$from" "$to" {} +

Використання -execdir

Є також -execdir(реалізований у більшості findваріантів, але не стандартний варіант).

Це працює на зразок -execз тією різницею, що дана команда оболонки виконується з каталогом знайденого імені шляху як його поточного робочого каталогу, і він {}буде містити базове ім'я знайденого імені шляху без його шляху (але GNU findвсе ще буде префіксувати базове ім'я ./, а BSD findне зробить цього).

Приклад:

find . -type f -name '*.txt' \
    -execdir mv {} done-texts/{}.done \;

Це перемістить кожен знайдений *.txtфайл у попередній done-textsпідкаталог у тому самому каталозі, де і був знайдений файл . Файл також буде перейменований, додавши до нього суфікс .done.

Це було б трохи складніше, -execоскільки нам слід було б отримати базове ім'я знайденого файлу, {}щоб сформувати нове ім'я файлу. Нам також потрібна назва каталогу, {}щоб done-textsправильно розмістити каталог.

З -execdirдеякими подібними речами стає простіше.

Відповідна операція, яка використовує -execзамість -execdir, повинна використовувати дочірню оболонку:

find . -type f -name '*.txt' -exec sh -c '
    for name do
        mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
    done' sh {} +

або,

find . -type f -name '*.txt' -exec sh -c '
    for name do
        mv "$name" "${name%/*}/done-texts/${name##*/}.done"
    done' sh {} +

7
-execбере програму та аргументи та запускає її; деякі команди оболонки складаються лише з програми та аргументів, але багато з них - ні. Команда оболонки може включати перенаправлення та трубопроводи; -execне може (хоча ціле findможна перенаправити). Команда оболонки може використовувати ; && ifтощо; -execне може, хоча -a -oможе зробити щось. Команда оболонки може бути псевдонімом або функцією оболонки, або вбудованою; -execне може. Команда оболонки може розширювати vars; -execне може (хоча зовнішня оболонка, що працює на findбанці). Команда оболонки може підміняти по- $(command)різному кожен раз; -execне може. ...
dave_thompson_085

... Команда оболонки може глобувати, -execне може - хоча findможе повторювати файли так само, як і більшість глобусів, тому цього рідко потрібно.
dave_thompson_085

@ dave_thompson_085 Звичайно, команда оболонки може бути shсамою собою, яка цілком здатна виконувати всі ці речі
Тавіан Барнс

2
Скажімо, що команда оболонки тут неправильна, find -exec cmd arg \;не викликає оболонку для інтерпретації командного рядка оболонки, вона запускається execlp("cmd", "arg")безпосередньо, а не execlp("sh", "-c", "cmd arg")(для чого оболонка в кінцевому підсумку робить еквівалент, execlp("cmd", "arg")якби cmdне була вбудована).
Stéphane Chazelas

2
Ви можете уточнити, що всі findаргументи після -execі до ;або +складають команду для виконання разом з її аргументами, при цьому кожен екземпляр {}аргументу замінюється поточним файлом (на ;) і {}як останній аргумент перед тим, як +замінюється списком файлів як окремі аргументи (у {} +випадку). IOW -execприймає кілька аргументів, припинених а ;чи {} +.
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.