Как я могу использовать синтаксис Bash в целях Makefile?

209

Я часто нахожу синтаксис Bash очень полезным, например, подстановка процесса, как вdiff <(sort file1) <(sort file2) .

Можно ли использовать такие команды Bash в Makefile? Я думаю о чем-то вроде этого:

file-differences:
    diff <(sort file1) <(sort file2) > $@

В моем GNU Make 3.80 это выдаст ошибку, так как он использует shellвместо bashвыполнения команд.

Фрэнк
источник
Это была именно моя проблема, мне потребовался не менее часа, чтобы найти этот вопрос! Я оставляю здесь свое сообщение об ошибке, чтобы будущие читатели могли его найти: /bin/sh: -c: line 0: syntax error near unexpected token ('`
Дэвид

Ответы:

381

Из GNU Make документацию,

5.3.1 Choosing the Shell
------------------------

The program used as the shell is taken from the variable `SHELL'.  If
this variable is not set in your makefile, the program `/bin/sh' is
used as the shell.

Так что поместите SHELL := /bin/bashв начало вашего make-файла, и вам следует идти дальше.

Кстати: вы также можете сделать это для одной цели, по крайней мере, для GNU Make. Каждая цель может иметь свои собственные назначения переменных, например:

all: a b

a:
    @echo "a is $$0"

b: SHELL:=/bin/bash   # HERE: this is setting the shell for b only
b:
    @echo "b is $$0"

Это напечатает:

a is /bin/sh
b is /bin/bash

См. «Значения переменных для конкретной цели» в документации для получения более подробной информации. Эта строка может идти в любом месте Makefile, она не должна быть непосредственно перед целью.

derobert
источник
43
500 щедрых жду цитату из man. Поговорим о времени. : P
SiddharthaRT
3
@inLoveWithPython Ну, infoвообще-то, но, думаю, это действительно помогло Энди. Я знаю, что у меня были такие дни ...
Дероберт
3
если сомневаетесь, @derobert имел в виду буквально: SHELL=/bin/bashкак первую строку Makefile (или сразу после комментария).
Евгений Якимович
1
Спасибо @derobert решил мою проблему в stackoverflow.com/questions/26806832/…
Чандан Чоудхури
2
Можно ли изменить переменную SHELL только для одной конкретной цели создания, но оставить остальные нетронутыми?
antred
18

Вы можете позвонить bashнапрямую, используя -cфлаг:

bash -c "diff <(sort file1) <(sort file2) > $@"

Конечно, вы, возможно, не сможете перенаправить на переменную $ @, но когда я попытался это сделать, я получил -bash: $@: ambiguous redirectсообщение об ошибке, так что вы, возможно, захотите разобраться в этом, прежде чем слишком углубиться в это (хотя я используя Bash 3.2. что-то, так что, возможно, ваш работает по-другому).

Крис Лутц
источник
4

Если важна переносимость, вы можете не зависеть от конкретной оболочки в вашем Makefile. Не во всех средах есть bash.

Менно Смитс
источник
4

Вы можете вызывать bash непосредственно в вашем Makefile вместо использования оболочки по умолчанию:

bash -c "ls -al"

вместо того:

ls -al
paxdiablo
источник
1
Обратите внимание, что makeигнорируется значение переменной среды SHELL.
choroba
2

Есть способ сделать это без явной установки переменной SHELL, указывающей на bash. Это может быть полезно, если у вас много make-файлов, поскольку SHELL не наследуется последующими make-файлами и не берется из среды. Вы также должны быть уверены, что любой, кто компилирует ваш код, настраивает свою систему таким образом.

Если вы запустите sudo dpkg-reconfigure dashи ответите «нет» на приглашение, ваша система не будет использовать тире в качестве оболочки по умолчанию. Затем он укажет на bash (по крайней мере, в Ubuntu). Обратите внимание, что использование dash в качестве системной оболочки немного более эффективно.

Билл Г
источник
1
При вызове под именем shbash работает в режиме совместимости ( set -o posix). Функциональность, которую пытается использовать ОП, процесс подстановки, недоступна в этом режиме.
Чарльз Даффи
1

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

your-target: $(eval SHELL:=/bin/bash)
    @echo "here shell is $$0"
Николас Маршалл
источник