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

3 ответа

Решение

С:

FILES = $(shell ls)

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

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

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

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

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

all:
        FILES="$(shell ls)"

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

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

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

        echo *

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

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


1 GNU Make отмечает в документации, что добавлен 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 ...) синтаксис и, следовательно, вызывая команду оболочки. Это замедлит работу или даже приведет к неожиданным последствиям.

Я пробовал использовать эту команду и ее работу в make v4.4.1.

      all:
    echo `pwd`
    echo `ls -al`
    echo `ls | grep 'filename'`
    cd ..; pwd
    pwd
    ls
    ls -al
    ls -al | grep 'exe'
Другие вопросы по тегам