Как использовать команды в 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), не повезло.

2 60

2 ответа:

С:

FILES = $(shell ls)

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

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

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

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

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

all:
        FILES="$(shell ls)"

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

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

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

        echo *

как ваша команда оболочки.

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


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

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

также, В дополнение к ответу торек: одна вещь, которая выделяется в том, что вы используете лениво-оценка макро назначения.

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

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

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

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