Каково различие между находкой с - должностное лицо и xargs?

попытка изучить сценарии Bash, я хочу выполнить некоторую команду на всех файлах ниже моего текущего каталога, которые удовлетворяют определенное условие. Используя

find -name *.flac

Конкретно я хочу преобразовать .flac кому: .mp3. Я могу найти все файлы. Однако я не вижу различия в выполнении команды с помощью любого опция -exec для find и использование xargs. Например.

find -name *.flac | xargs -i ffmpeg -i {} {}.mp3

по сравнению с

find -name *.flac -exec ffmpeg -i {} {}.mp3 \;

Кто-то может указать на различие? Что такое лучший praticice? Каковы преимущества / недостатки?

Также: Если бы я хотел одновременно удалить исходный файл, как я включил бы вторую команду вышеупомянутый код?

7
задан 25 September 2017 в 09:40

3 ответа

Сводка:

Если Вы не намного более знакомы с xargs чем -exec, Вы, вероятно, захотите использовать -exec когда Вы используете find.

С тех пор xargs отдельная программа, называние ее, вероятно, будет незначительно менее эффективным, чем использование -exec, который является функцией find программа. Мы обычно не хотим называть дополнительную программу, если она не предоставляет дополнительного преимущества с точки зрения надежности, производительности или удобочитаемости. С тех пор find ... -exec ... обеспечивает способность к командам выполнения со списком аргументов (как xargs делает), если это возможно, нет действительно никакого преимущества использования xargs с find -exec. В случае ffmpeg, мы должны указать входные и выходные файлы, таким образом, мы не можем сделать увеличение производительности с помощью любого метода для построения списка аргументов, и с xargs удаление нелогичного исходного расширения файла является более трудным.

Что xargs делает

Примечание: Подробный флаг (который печатает созданную команду с ее аргументами) в xargs -t, и интерактивный флаг (который заставляет пользователя быть предложенным подтверждение воздействовать на каждый аргумент) -p. Можно найти оба из них полезными для понимания и тестирования его поведения.

xargs попытки повернуть его STDIN (обычно STDOUT предыдущей команды, которая была передана по каналу к нему) в список аргументов некоторой команде.

command1 | xargs command2 [output of command1 will be appended here]

Начиная с STDOUT или STDIN просто поток текста (это также, почему Вы не должны анализировать вывод ls), xargs легко сбит с толку. Это читает аргументы, как разграничиваемые пробелами или новыми строками. Именам файлов позволяют содержать пробелы и могут даже содержать новые строки, и такие имена файлов вызовут неожиданное поведение. Скажем, Вам назвали файл foo bar. Когда список, содержащий это имя файла, передается по каналу к xargs, это пытается работать на данной команде foo и на bar.

Та же проблема происходит, когда Вы вводите command foo bar, и Вы знаете, что можно избежать его путем заключения в кавычки пространства или целого имени, например, command foo\ bar или command "foo bar", но даже если мы можем заключить в кавычки список, переданный xargs мы обычно не хотим, потому что мы не хотим, чтобы целый список рассматривался как отдельный аргумент. Стандартное решение этого состоит в том, чтобы использовать нулевой символ в качестве разделителя, так как имена файлов не могут содержать его:

find path test(s) -print0 | xargs -0 command

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

Проблемы могут все еще произойти, если команда не принимает несколько аргументов или если список аргументов чрезвычайно длинен.

В этом случае Вы используете ffmpeg, который ожидает, что входные файлы будут указаны сначала, и выходные файлы, которые будут указаны в последний раз. Мы можем сказать ffmpeg какие файлы (файлы) использовать в качестве входа явно с -i флаг, но мы должны дать выходное имя файла (от которого обычно предполагается формат, хотя мы можем также указать его), также. Так, для построения подходящих команд необходимо использовать опцию строки замены (-I или -i) из xargs указывать обоих входные и выходные файлы:

... | xargs -I{} command {} {}.out

(в документации говорится это -i удерживается от использования с этой целью, и мы должны использовать -I вместо этого, но я не уверен почему. При использовании -I, необходимо указать замену ({} обычно используется), сразу после опции. С -i можно опустить указывать замену, но {} понят по умолчанию.)

-I опция заставляет список команд быть разделенным только на новых строках, не пробелах, поэтому если Вы уверены, что Ваши имена файлов не будут содержать новые строки, Вы не должны использовать -print0 | xargs -0 когда Вы используете -I. Если Вы не уверены, можно все еще использовать более безопасный синтаксис:

find -name "*.flac" -print0 | xargs -0I{} ffmpeg -i {} {}.mp3

Однако выигрыш в производительности xargs (который позволяет нам работать, команда однажды со списком аргументов) потерян здесь, с тех пор ffmpeg должен быть выполнен однажды для каждой пары входных и выходных файлов (Вы видите это легко путем предварительного ожидания echo кому: ffmpeg протестировать вышеупомянутую команду). Это также производит нелогичное имя файла и не позволяет Вам выполнять несколько команд. Чтобы сделать последнего, можно звонить bash, как в ответе десерта:

... | xargs -I{} bash -c 'ffmpeg -i {} {}.mp3 && rm {}'

но переименование хитро.

Как -exec отличается

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

find ... -exec command {} \;

command выполняется однажды для каждого найденного файла. С синтаксисом

find ... -exec command {} +

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

С ffmpeg, мы не можем использовать + по той же причине как xargs не дал выигрыша в производительности; так как мы должны указать оба ввода и вывода, команда должна быть выполнена на каждом файле индивидуально. Мы должны использовать некоторую форму

find -name "*.flac" -exec ffmpeg -i {} {}.out \;

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

Вместо того, чтобы повторить рекомендацию десерта, с которой я соглашаюсь, я предложу альтернативу find, который позволяет подобную гибкость выполнению bash -c после -exec; удар for цикл:

shopt -s globstar           # allow recursive globbing with **
for f in ./**/*.flac; do    # for all files ending with .flac
   # convert them, stripping the original extension from the new filename
   echo ffmpeg -i "$f" "${f%.flac}.mp3" &&
   echo rm -v "$f"          # if that succeeded, delete the original
done
shopt -u globstar           # turn recursive globbing off

Удалите echoes после тестирования для фактической работы на файлы.

ffmpeg не распознает -- отметить конец опций, так для предотвращения начала имен файлов - будучи интерпретированным как опции, мы используем ./ указать на текущий каталог вместо того, чтобы запуститься с **, так, чтобы все пути начались ./ вместо произвольных имен файлов. Это означает, что мы не должны использовать -- с rm (который действительно распознает его), также.


Примечание: необходимо заключить Ваш в кавычки -name проверяемое выражение, если это содержит какие-либо подстановочные символы, иначе оболочка, развернет их, если возможный (т.е. если они соответствуют любым файлам в текущем каталоге), прежде чем они будут переданы find, так во-первых использовать

find -name "*.flac"

предотвратить неожиданное поведение.

10
ответ дан 23 November 2019 в 06:14

Обычно каждый пытается назвать как можно меньше команд, но в Вашем случае я думаю, что это - вопрос вкуса – я пошел бы с -exec, использование его как так:

find . -name '*.flac' -exec bash -c 'ffmpeg -i "$0" "${0%flac}mp3" && rm "$0"' {} \;

Прием должен звонить bash с -c опция, этот путь можно не только выполнить несколько команд, но также и использовать Замену Параметра Bash для удаления flac при окончании от имен файлов – я предполагаю, что Вы действительно не хотите заканчивать с файлами под названием filename.flac.mp3, не так ли?

Объяснения

  • bash -c '…' {} – выполните команду (команды) в bash с именем файла как первый аргумент (доступный с $0)
  • ${0%flac} – полоса flac от конца имени файла
  • && rm "$0" – только если предыдущая команда, за которой следуют, удалите исходный файл
4
ответ дан 23 November 2019 в 06:14

Как Zanna и десерт, которому уже отвечают -exec должен быть предпочтен когда xargs не необходимо ("Мы обычно не хотим называть дополнительную программу, если она не предоставляет дополнительного преимущества с точки зрения надежности, производительности или удобочитаемости".)

В то время как это полностью корректно, я хочу добавить это xargs в сочетании с -P флаг может предоставить существенное преимущество с точки зрения производительности.

xargs породит процессы в параллельной многопоточности включения, подобной, но более гибкий, чем parallel команда.

-P max-procs, --max-procs=max-procs
              Run up to max-procs processes at a time; the default is 1.  If max-procs is 0, xargs will run as many processes as possible at a time.  Use the -n option or the -L option with -P; other‐
              wise chances are that only one exec will be done. 
              [...]

Это особенно помогает с с процессами, которые не работают многопоточный собой. В Вашем случае ffmpeg будет заботиться о многопоточности, таким образом, она не поможет или будет даже иметь отрицательный эффект на производительность.

find . -name "*.ext" -print0 | xargs -0 -i -P 20 command -in {} -out {}.out
2
ответ дан 23 November 2019 в 06:14

Другие вопросы по тегам:

Похожие вопросы: