Jak mogę używać składni Bash w obiektach Makefile?

208

Często uważam, że składnia Bash jest bardzo pomocna, np. Podstawianie procesów jak w diff <(sort file1) <(sort file2).

Czy można używać takich poleceń Bash w pliku Makefile? Mam na myśli coś takiego:

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

W moim GNU Make 3.80 spowoduje to błąd, ponieważ używa shellzamiast zamiast bashdo wykonywania poleceń.

Szczery
źródło
To był dokładnie mój problem, zajęło mi co najmniej godzinę, aby znaleźć to pytanie! Zostawiam tutaj swój komunikat o błędzie, aby przyszli czytelnicy mogli go znaleźć: /bin/sh: -c: line 0: syntax error near unexpected token (''
David

Odpowiedzi:

380

Z dokumentacji 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.

Więc umieść SHELL := /bin/bashna szczycie swojego makefile, a powinieneś być gotowy.

BTW: Możesz to zrobić również dla jednego celu, przynajmniej dla GNU Make. Każdy cel może mieć własne przypisania zmiennych, takie jak to:

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"

To wydrukuje:

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

Aby uzyskać więcej informacji, zobacz „Wartości zmiennych specyficznych dla celu” w dokumentacji. Ta linia może iść w dowolne miejsce w pliku Makefile, nie musi znajdować się bezpośrednio przed celem.

derobert
źródło
43
500 nagród czeka na wycenę od man. Rozmawiaj o czasach. : P
SiddharthaRT
3
@inLoveWithPython Cóż, infowłaściwie, ale myślę, że to naprawdę pomogło Andy'emu. Wiem, że miałem takie dni ...
derobert
3
w razie wątpliwości @derobert oznaczał dosłownie: SHELL=/bin/bashjako pierwszy wiersz pliku Makefile (lub zaraz po komentarzu).
Yauhen Yakimovich
1
Dzięki @derobert rozwiązał mój problem w stackoverflow.com/questions/26806832/…
Chandan Choudhury
2
Czy można zmienić zmienną SHELL tylko dla jednego konkretnego celu make, ale pozostawiając pozostałe nietknięte?
antred 4.04.16
18

Możesz zadzwonić bashbezpośrednio, użyj -cflagi:

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

Oczywiście możesz nie być w stanie przekierować do zmiennej $ @, ale kiedy próbowałem to zrobić, dostałem -bash: $@: ambiguous redirectkomunikat o błędzie, więc możesz przyjrzeć się temu, zanim się w to wnikniesz (chociaż jestem używając bash 3.2. coś, więc może twój działa inaczej).

Chris Lutz
źródło
4

Jeśli przenośność jest ważna, możesz nie chcieć polegać na konkretnej powłoce w pliku Makefile. Nie we wszystkich środowiskach dostępna jest wersja bash.

Menno Smits
źródło
4

Możesz wywołać bash bezpośrednio w swoim Makefile zamiast używać domyślnej powłoki:

bash -c "ls -al"

zamiast:

ls -al
paxdiablo
źródło
1
Zauważ, że makeignoruje wartość zmiennej środowiskowej SHELL.
choroba
2

Jest na to sposób bez jawnego ustawiania zmiennej SHELL tak, aby wskazywała bash. Może to być przydatne, jeśli masz wiele plików makefile, ponieważ SHELL nie jest dziedziczony przez kolejne pliki makefile ani pobierany ze środowiska. Musisz także mieć pewność, że każdy, kto skompiluje Twój kod, skonfiguruje swój system w ten sposób.

Jeśli uruchomisz sudo dpkg-reconfigure dashi odpowiesz „nie” na monit, system nie użyje myślnika jako domyślnej powłoki. Następnie wskaże bash (przynajmniej w Ubuntu). Zauważ, że użycie myślnika jako powłoki systemowej jest nieco bardziej wydajne.

Bill G.
źródło
1
Po wywołaniu pod nazwą shbash działa w trybie zgodności ( set -o posix). Funkcja, z której próbuje korzystać OP, zastępowanie procesów, nie jest dostępna w tym trybie.
Charles Duffy
1

Jednym ze sposobów, który również działa, jest umieszczenie go w ten sposób w pierwszej linii celu:

your-target: $(eval SHELL:=/bin/bash)
    @echo "here shell is $$0"
Nicolas Marshall
źródło