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