Вопрос или проблема
Я хотел бы узнать, возможно ли написать совместимый с POSIX скрипт, который строит команду find
из её аргументов.
Способ сделать это для Bash (и zsh) объясняется в этой другой статье SE, но она использует средства, специфичные для Bash (или zsh).
Исходный вопрос заключался в том, чтобы иметь возможность строить команду find, подобную этой:
find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
Ответ представлен в следующем коде скрипта Bash:
findany() {
local args=('-iname' "*$1*")
shift
while [ "$#" -gt 0 ]; do
args+=('-o' '-iname' "*$1*")
shift
done
find . "${args[@]}"
}
Возможно ли сделать что-то подобное в совместимом с POSIX скрипте?
В оболочках POSIX есть только один массив – массив аргументов. Вы могли бы использовать это здесь:
findany() {
n=$# # Сохранить исходное количество аргументов
if [ "$n" -gt 0 ]
then
for arg # Цикл по аргументам функции
do
# Перезаписать аргументы с существующими аргументами + дополнительными опциями
# с текущим обрабатываемым аргументом
set -- "$@" -o -iname "*$arg*"
done
# Удалить исходный набор аргументов и `-o` после них,
# оставив только `-iname ...` аргументы.
shift $((n+1))
fi
# Передать аргументы `-iname ...`, если они есть, в find
find . "$@"
}
Это должно работать и в bash, и в zsh.
Ответ или решение
Вопрос о том, возможно ли создать POSIX-совместимый скрипт для динамического построения команды find
из аргументов, является интересной задачей для разработчиков, работающих с оболочками Unix. В данной статье мы подробно рассмотрим, как это можно сделать, следуя принципам POSIX, и, в частности, разберем реализацию функции findany
.
Задача
Построить команду find
, которая будет использовать оператор ИЛИ (-o
) для поиска файлов с несколькими шаблонами имен. Например:
find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
Реализация POSIX-совместимого скрипта
В POSIX-совместимых оболочках, таких как sh
, есть ограничение в использовании массивов, что требует другого подхода к построению команд. Мы можем использовать встроенную функцию set
для управления списками аргументов.
Вот пример реализации функции findany
, которая принимает список строк и динамически строит команду find
:
findany() {
n=$# # Сохраняем первоначальное количество аргументов
if [ "$n" -gt 0 ]; then
for arg; do # Пробегаем по всем аргументам функции
# Используем set для добавления аргументов ИЛИ
set -- "$@" -o -iname "*$arg*"
done
# Убираем оригинальные аргументы и оператор -o
shift $((n + 1))
fi
# Передаем аргументы -iname в команду find
find . "$@"
}
Объяснение работы функции
-
Сохранение количества аргументов: Переменная
n
сохраняет количество аргументов, переданных функции. Это необходимо для дальнейшего управления аргументами. -
Итерация по аргументам: С помощью цикла
for arg; do
мы перебираем каждый аргумент, который будет передан функции. -
Построение команды: Внутри цикла мы используем
set -- "$@" -o -iname "*$arg*"
для добавления новых условий поиска."$@"
представляет собой текущий набор аргументов, а-o -iname "*$arg*"
добавляет условие для поиска. -
Удаление оригинальных аргументов: После завершения итерации мы используем
shift $((n + 1))
для удаления первоначальных аргументов и лишнего-o
, оставляя только аргументы с-iname
. -
Вызов команды
find
: В завершение мы вызываем командуfind
, передавая текущий набор аргументов, который теперь содержит только условия поиска.
Пример использования
Теперь, чтобы использовать нашу функцию, мы можем выполнить следующие команды в оболочке:
$ findany foo bar blah
Это сгенерирует команду, аналогичную:
find . -o -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
Заключение
Данный подход позволяет динамически генерировать команды find
в POSIX-совместимой среде, предоставляя разработчикам гибкость в работе с файловыми системами. Теперь вы можете легко адаптировать этот пример к вашим потребностям и расширять его функциональность.
Если у вас возникли дополнительные вопросы или требуется помощь в деталях реализации, не стесняйтесь обращаться за помощью.