find -exec у скрипті bash зі змінним розширенням


14

Я намагаюся запустити команду, подібну до наведеної нижче, в скрипті bash. Він повинен шукати всі підпапки $sourcedirта копіювати всі файли певного типу на кореневий рівень $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Це , здається, досить близько, за винятком того, що {}не передається як $2до-exec sh -c ...

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

Редагувати: я бачу людей, які пропонують використовувати xargsабо ланцюжок аргументів. У мене було враження, що для обмеженої кількості аргументів це нормально. Якщо у мене, наприклад, є тисячі файлів .jpg, які я намагаюсь скопіювати з декількох каталогів галереї у гігантський каталог слайд-шоу, чи все ж таки ланцюжки аргументів можуть працювати?

Редагувати 2: Моя проблема полягала в тому, що я не вистачав _до мого першого варіанту, щоб перейти до -execкоманди. Для тих, хто цікавиться, як змусити команду find працювати, додайте _і все буде добре:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Я прийняв відповідь нижче, хоча тому, що вона виконує те саме завдання, але є більш ефективною та елегантною.


4
Саме тому xargsбуло створено коли-небудь автоматичне обробку величезної кількості аргументів на звичайні команди, які мали обмеження. Також слід врахувати, що більшість максимальних обмежень аргументів значно покращені для стандартних утиліт GNU. Ви також побачите перевагу від продуктивності, уникаючи всіх тих вилок процесів, які на тисячах файлів є релевантними.
Дж. М. Бекер

За допомогою gnu-find та + замість ";" ви можете обробити декілька аргументів одночасно і з find. І ви зберігаєте складний аргумент, передаючи за допомогою -print0.
користувач невідомий

@userunknown: Я відповідаю на це нижче вашої відповіді.
Дж. М. Бекер

@user unknown Не знаю, я люблю цей код. Він принаймні повністю сумісний з POSIX і буде працювати без будь-яких GNU-матеріалів на машині взагалі. Є такі часи, коли вам це потрібно, особливо на серверах на роботі.
синтаксис-помилка

Відповіді:


6

Ви хочете скопіювати файли певного типу у певний каталог? Це найкраще робити xargs, і вам це навіть не потрібно sh. Це більш підходящий спосіб зробити це, також слід працювати більш ефективно.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Якщо вам потрібно обробити спеціальні імена файлів, тоді використовуйте їх NULLяк сепаратор

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"

1
Для другого випадку не забудьте додати -print0до findта -0доxargs
SiegeX

@ SiegeX, вже робив це, перш ніж я помітив ваш коментар.
Дж. М. Бекер

Крім того, NULLце не потрібно, якщо ви використовуєте '{}', це важливо, коли ви цього не робите. Справжньою вигодою між одним, окрім POSIXвідповідності, є продуктивність.
Дж. М. Бекер

6

Вам потрібно передати {}як аргумент оболонці, а потім петлю над кожним аргументом.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Примітка . Як це працює в тому, що перший аргумент оболонки - це назва оболонки , ми можемо використовувати це, передаючи ім'я як $targetdirі потім використовувати спеціальний параметр $0всередині скрипту оболонки для доступу до цього targetdir.


1
"$targetdir"не розширюється всередині одиничних лапок.
enzotib

5

Якщо ви не вірите в церкву xargs:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Пояснення:

cp -t a b c d 

копії b, c і d до цільового режиму a.

-exec cmd {} +

викликає команду на великій купі файлів відразу, а не один за одним (що є звичайним, якщо ви використовуєте ";"замість +). Ось чому доведеться витягнути targetdir на фронт і чітко позначити його як ціль.

Це працює для gnu-find та не може застосовуватися для інших реалізацій знаходження. Звичайно, він також покладається на -t -flag.

TechZilla, звичайно, настільки право, як shце не потрібно для виклику cp.

Якщо ви не використовуєте xargs, що в більшості випадків непотрібно в поєднанні з find, ви врятуєтесь від вивчення -print0та -0прапора.


1
Очевидно, який метод хтось може віддати перевагу, є дещо суб'єктивним. З урахуванням сказаного, існують причини, які все-таки використовувати xargs. Найбільший приклад: що робити, якщо ви не знаходите файли? Я використовую xargs замість загальних forциклів весь час, findнабагато менший за обсягом. Крім того, findпідтримується лише те, що +якщо ви використовуєте GNU find, це не визначено в POSIX. Тож, хоча вам слід віддати перевагу findнаодинці, це не все, що робить xargs. Якщо врахувати GNU, xargsви також отримаєте -Pбагатоядерний. Варто навчитися xargsнезалежно.
Дж. М. Бекер

Суб'єктивність, кажучи, на мою думку, очевидно, відрізняється. З іншого боку, ваша відповідь також правильна. Це одне з небагатьох найкращих доступних рішень.
Дж. М. Бекер

@TechZilla: Я сподіваюся не забувати переглянути цей сайт, коли пошук почне підтримувати паралельне виклик. :) У більшості випадків при копіюванні / переміщенні швидкість диска буде обмежувальним фактором, але SSD можуть змінити зображення. Ви маєте рацію, що не-GNU-рішення надати - це чудова річ. У іншому випадку рішення пошуку об'єктивно коротше, простіше і використовує лише два процеси.
користувач невідомий

0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;

3
Розгляньте можливість додати пояснення свого рішення.
HalosGhost
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.