В материале о том, что в наши дни find
, как правило, не нуждается в xargs
, я отметил, что в конструкции '-exec ... {} +'
скобки ('{}'
) (для имён файлов, генерируемых find
) должны находиться в конце команды. В комментарии к той публикации анонимный читатель сказал, что это неприменимо к -exec-версии, которая запускает отдельную команду для каждого имени файла. В результате можно поместить заменяемое имя файла в любом месте команды. Это, как оказалось, относится не только к GNU Find, являясь стандартной возможностью, и я полагаю, что этого даже требует Single Unix Specification (SUS) для find.
(SUS, в отношении аргументов -exec
, вводит ограничения лишь на форму '+'
, предписывая размещать '{}'
непосредственно перед '+'
. Спецификация же для формы ';'
просто говорит об использовании обычного списка аргументов, после чего в описании сказано, что '{}'
в списке аргументов заменяется на текущий путь. Понять это всё довольно сложно, хотя подобное в SUS и POSIX — обычное дело.)
Разница между двумя формами -exec
— это интересный вопрос, и эта разница, вероятно, существует из-за удобства реализации формы '+'
. Поэтому предлагаю начать с самого начала. Когда применяют любую из форм -exec
, команда find
выполняет соответствующие команды, задействуя семейство системных вызовов exec()
(и библиотечные функции), которым нужно, чтобы им передавался бы © массив с командами и с аргументами (то есть — это argv
для новой команды). Реализация этого в конструкции, где выполняется одна замена ('-exec ... ;'
) проста: создают и заполняют массив argv
, содержащий все аргументы -exec
(и команды), и запоминают индекс параметра '{}'
(если такой параметр есть; он не является обязательным). Каждый раз, когда выполняют команду, текущий путь помещают в ячейку argv
и дело сделано.
При использовании ограниченной формы множественной замены это, в некотором роде, тоже можно сделать. Создаётся массив argv
некоего размера, начало которого заполняют всеми фиксированными опциями, а потом присоединяют каждый путь к его концу в виде дополнительной опции, следя за общим размером всех аргументов до тех пор, пока не понадобится выполнить команду, что позволяет ограничивать размер массива. Когда всё готово, индекс, по которому должен быть размещён следующий путь, сбрасывается к стартовой позиции, к месту, где заканчиваются фиксированные опции, после чего всё повторяется.
Правда, если конструкция '{}'
может размещаться где угодно — понадобится более сложная реализация, в которой надо будет делить фиксированные аргументы на две части, одна из которых идёт до '{}'
, а вторая — после. Начало argv
тогда заполняется «предшествующими» фиксированными аргументами, потом в массив попадают пути, в виде дополнительных аргументов, до достижения лимита, а затем, до exec()
, присоединяются «следующие» фиксированные аргументы, если такие имеются. Для реализации этого всего нужно не так уж и много дополнительного труда, но над этим, всё равно, надо поработать. Поэтому я вынужден выдвинуть теорию о том, что объём этого дополнительного труда оказался именно таким, чтобы программисты, реализующие команду в System V R4 (там эта возможность появилась впервые) выбрали бы ограниченную форму ради удобства разработки и ради того, чтобы в их коде было бы меньше ошибок (ведь код, который не надо писать, определённо, совсем не содержит ошибок).
(Уверен, что это — не единственная область Unix-команд, в которой можно заметить признаки стремления разработчиков к реализации того, что реализовывать удобнее. Но в случае с двумя версиями -exec
команды find
перед нами — яркий пример такого подхода.)
Сталкивались ли вы с какими-то особенностями Unix-команд, которые можно объяснить стремлением их создателей к удобству их реализации?
Автор: ru_vds