Подтвердить что ты не робот

Как использовать команды оболочки в Makefile

Я пытаюсь использовать результат ls в других командах (например, echo, rsync):

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

Но я получаю:

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

Я пробовал использовать echo $$FILES, echo ${FILES} и echo $(FILES), не повезло.

4b9b3361

Ответ 1

С

FILES = $(shell ls)

с отступом под all, как это, это команда сборки. Таким образом, это расширяет $(shell ls), затем пытается запустить команду FILES ....

Если FILES предполагается переменной make, эти переменные должны быть назначены вне части рецепта, например:

FILES = $(shell ls)
all:
        echo $(FILES)

Конечно, это означает, что FILES будет настроен на "вывод из ls" перед запуском любой из команд, которые создают файлы .tgz. (Хотя в качестве примечаний Kaz переменная повторно расширяется каждый раз, поэтому в конечном итоге она будет включать файлы .tgz, а некоторые из вариантов имеют FILES := ..., чтобы избежать этого, для эффективность и/или правильность. 1)

Если FILES предполагается как переменная оболочки, вы можете установить его, но вам нужно сделать это в shell-ese без пробелов и процитировать:

all:
        FILES="$(shell ls)"

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

        FILES="$(shell ls)"; echo $$FILES

Это немного глупо, так как оболочка будет расширять * (и другие выражения оболочки оболочки) для вас в первую очередь, поэтому вы можете просто:

        echo *

в качестве вашей команды оболочки.

Наконец, как общее правило (на самом деле не применимое к этому примеру): как esperanto примечания в комментариях, использование вывода из ls не полностью (некоторые детали зависят от имен файлов, а иногда и от версии ls, некоторые версии ls пытаются в какой-то мере дезинформировать вывод). Таким образом, как l0b0 и idelic обратите внимание, что если вы используете GNU make, вы можете использовать $(wildcard) и $(subst ...) для выполнения всего внутри make (избегая проблем с "странными символами в имени файла" ). (В сценариях sh, включая часть рецептов make файлов, другой метод заключается в использовании find ... -print0 | xargs -0, чтобы избежать отключения над пробелами, символами новой строки, управляющими символами и т.д.)


1GNU Сделайте еще документацию о том, что POSIX добавляет ::= в 2012 году. Я не нашел ссылку для ссылки на документ POSIX для этого, и я не знаю, что поддерживает make варианты ::=, хотя GNU make делает сегодня с тем же значением, что и :=, т.е. выполните задание прямо сейчас с расширением.

Обратите внимание, что VAR := $(shell command args...) также может быть записано VAR != command args... в нескольких вариантах make, включая все современные варианты GNU и BSD, насколько я знаю. Эти другие варианты не имеют $(shell), поэтому использование VAR != command args... превосходит как сокращение, так и работу в большем количестве вариантов.

Ответ 2

Кроме того, в дополнение к ответу torek: одна вещь, которая выделяется, заключается в том, что вы используете лениво оцениваемое назначение макроса.

Если вы используете GNU Make, используйте := вместо =. Это назначение заставляет правую сторону немедленно разворачиваться и храниться в левой переменной.

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

Если вы используете назначение =, это означает, что каждое единственное вхождение $(FILES) будет расширять синтаксис $(shell ...) и, таким образом, вызывать команду оболочки. Это заставит вашу работу работать медленнее или даже иметь некоторые неожиданные последствия.