Бэширование многопараметрического аргумента

Ниже приведены примеры команды find которую я пытаюсь запустить. Я ищу различные способы, которыми я мог бы использовать globbing для генерации (в качестве примера) команды find с объединенными предикатами.

Не работает, потому что find требует a -name перед каждым и -o между ними.

 find . -name \*.{sh,pl,sql} #find . -name *.sh *.pl *.sql 

Не работает из-за trailing -o . Мог бы получить гарантированный отказ от аргументов, но не идеален. Кроме того, мой ярлык теперь длиннее моего вывода.

 find . `for X in {sh,pl,sql}; do echo -name \\\*.$X -o ;done` #find . -name \*.sh -o -name \*.pl -o -name \*.sql -o 

Сбой, потому что они сгруппированы как один аргумент ( find: unknown predicate '-name *.sh' ). Также, несмотря на отсутствие соединения с -o.

 find . -name\ \*.{sh,pl,sql} 

Работает, но не включает подталкивание (re: not-answer):

 find . -regex '.*\(sh\|pl\|sql\)' 

« -false идея» – это ключ, ИМХО. Я просто добавляю к нему:

 find . -false $(echo "-o -name *."{sh,pl,sql}) 

Вы просто цитируете все, чтобы bash повторил весь шаблон, включая -o -name , а затем «сломал» группировку, сделанную путем цитирования, вернув ее из подоболочки. Проблема с этим подходом заключается в том, что кавычки в шаблоне не будут работать.


EDIT: см. Комментарий Michał Šrajer для другой ошибки этого решения. Обратите внимание, что вы не можете просто поставить обратную косую черту перед звездой: подстановка команды возвращает либо * , который будет расширяться, либо \* , который будет передан как есть – найти (!). По крайней мере, так работает мой местный баш.

Это лучшее, что я могу сделать:

  (GLOBIGNORE='*:.*'; find . \( -false $(echo "-o -name *."{sh,pl,sql,xml}) \) -print) 

Удачи 🙂

Кстати, если вы не собираетесь добавлять больше параметров для find , конечно, просто используйте xargs ; в этом случае он работает отлично, с котировками и всего:

 echo "-o -name *."{sh,pl,sql} | xargs find . -false 

find не использует glob() . Он использует fnmatch() . Вот почему единственные метасимволы, доступные в предикате in -name – это * -name , [ и ] .

Чтобы решить trailing -o я предлагаю положить его в скобки и добавить дополнительный -false предикат:

 ( -name AAA -o -name BBB -o -name CCC -o -false ) 

Я предполагаю, что find здесь является примером, и вас не интересует решение, которое позволяет объединять шаблоны в один аргумент find (это будет -regex ). Существует два возможных подхода: вы можете построить строку и позволить оболочке разбивать ее на несколько аргументов везде, где есть пробелы, или вы можете построить массив.

С подходом строкового построения, используя подстановку команд, это не намного проще, чем ваша попытка.

 find . $(for X in sh pl sql; do echo "-name \\*.$X" -o; done | sed 's/-o *$//') 

Вы также можете построить строку в цикле перед командой find ; Я думаю, что это сделало бы более четкий сценарий¹.

 find_options= for x in sh pl sql; do find_options="$find_options -o -name \\*.$x" done find_options=${find_options# -o} find . $find_options 

Если ваша оболочка поддерживает массивы (т. Е. Это bash или ksh или zsh), я рекомендую использовать их вместо этого, поэтому вам не придется вставлять лишние кавычки.

 find_options=() for x in sh pl sql; do find_options+=(-o -name "*.$x") done unset "find_options[0]" find . "${find_options[@]}" 

¹ В командной строке вам не нужно беспокоиться о переносимости и процитировать как можно меньше, так как вам не нужно беспокоиться о вводе пользователя. И вы можете обрабатывать повторение с помощью copy-paste.