bash отменить / отредактировать часть glob (скрипт не работает, если часть glob не найдена)

Я пишу сценарий на BASH, который в основном действует как упрощенный конструктор пакетов и gmake. Он поддерживает только один язык; это сделано намеренно. Я не собираюсь публиковать этот скрипт, так как он предназначен только для локального использования.

У меня в основном все работает правильно. На более сложных проектах все справлялось нормально. Проблема возникла при выполнении простой тестовой программы.

Сценарий создает «аргументы» до разумных значений по умолчанию с основными параметрами. Независимо от «режима» скрипт всегда выполняет строку следующего вида:

«compiler» «args» *.«ext»

Учитывая, что поддерживаемый язык (Ada) имеет два расширения файлов, следующий глобус используется при компиляции как «ext»:

*.{ads,adb}

Когда существуют оба расширения, как и следовало ожидать в более крупных проектах, все в порядке. Но сценарий не работает, и компиляция не происходит, если в $ PWD существует только одно из расширений.

Меня это довольно смущает, потому что существует "чистый режим", который просто выполняет:

rm *.{ali,o,so}

и это работает, даже если некоторые из перечисленных расширений не существуют в $ PWD.

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

Я думаю, можно было бы создать глобус в начале сценария о том, какие расширения существуют в $ PWD, а затем передать глобус компилятору. Однако я совершенно не представляю, как этого добиться. При некоторых попытках bash жалуется, что команда "*. {Adb, ads}" не может быть найдена. Это действительно было для меня сюрпризом, поскольку я, по сути, просто делал:

«compiler» «args» $FoundFiles

person Patrick Kelly    schedule 04.04.2015    source источник


Ответы (2)


Раскладка скобок превращает *.{adb,ads} в два слова, *.adb и *.ads. Затем каждый из них (независимо) расширяется глобусом. Если расширение glob терпит неудачу, то глобус передается без изменений, а не удаляется. Например, если у вас нет файлов с расширением ads, вы можете попытаться скомпилировать f1.adb f2.adb *.ads. Поскольку последнего из них не существует, компилятор пожалуется.

То же самое происходит с rm *.{ali,o,so}, и rm будет жаловаться и остановится, если один из шаблонов пройдет через него без изменений. Но если команда действительно rm -f *.{ali,o,so}, она будет выполняться нормально, потому что флаг -f указывает rm игнорировать отсутствующие файлы (среди прочего).

Вы можете изменить обработку bash несовпадающих глобусов, установив параметр оболочки nullglob (shopt -s nullglob); с этим набором опций несовпадающие глобусы удаляются из списка слов вместо того, чтобы проходить через него без изменений.

Однако в этом конкретном случае вы также можете использовать

compile *.ad[bs]

который представляет собой одиночный глобус с теми же совпадениями, что и пара глобусов, раскрытых в фигурные скобки. Это тоже будет передано в неизмененном виде, если оно ни с чем не соответствует, но это будет означать, что файлов Ada вообще нет, что должно вызвать сообщение об ошибке. Если установлено nullglob, конечным результатом будет вызов компилятора без аргументов; это, вероятно, также вызовет сообщение об ошибке, но некоторые утилиты будут обрабатывать стандартный ввод, если им не предоставлены какие-либо аргументы, и вы должны быть осторожны с nullglob в таких случаях.

person rici    schedule 04.04.2015
comment
Прочтите все руководство BASH и, должно быть, пропустили вставки скобок. - person Patrick Kelly; 04.04.2015

Используйте 1_:

shopt -s nullglob
compiler other_args *.{ads,adb}

Объяснение

Давайте использовать каталог, в котором есть один файл .ads, но нет файлов .adb:

$ echo *.{ads,adb}
1.ads *.adb

По умолчанию, если ни один файл не соответствует глобу, bash вернет сам глобус, в данном случае *.adb. Параметр nullglob изменяет это, и глобус без совпадений будет опущен. Таким образом:

$ shopt -s nullglob
$ echo *.{ads,adb}
1.ads

Чтобы отключить nullglob, используйте:

shopt -u nullglob
person John1024    schedule 04.04.2015