Mam bardzo wygodny sposób kompilacji projektu za pomocą kilku wierszy poleceń basha. Ale teraz muszę go skompilować za pomocą makefile. Biorąc pod uwagę, że każde polecenie jest uruchamiane we własnej powłoce, moje pytanie brzmi: jaki jest najlepszy sposób na uruchomienie wielowierszowego polecenia bash, zależnego od siebie, w makefile? Na przykład tak:
for i in `find`
do
all="$all $i"
done
gcc $all
Czy ktoś może też wyjaśnić, dlaczego nawet jednoliniowe polecenie bash -c 'a=3; echo $a > file'
działa poprawnie w terminalu, ale tworzy pusty plik w przypadku makefile?
Odpowiedzi:
Możesz użyć odwrotnego ukośnika do kontynuacji wiersza. Zwróć jednak uwagę, że powłoka otrzymuje całe polecenie połączone w jedną linię, więc musisz również zakończyć niektóre wiersze średnikiem:
Ale jeśli chcesz po prostu wziąć całą listę zwróconą przez
find
wywołanie i przekazać jągcc
, w rzeczywistości niekoniecznie potrzebujesz polecenia wielowierszowego:Lub używając bardziej konwencjonalnego
$(command)
podejścia (zauważ$
ucieczkę):źródło
Jak wskazano w pytaniu, każde polecenie podrzędne jest uruchamiane we własnej powłoce . To sprawia, że pisanie nietrywialnych skryptów powłoki jest trochę bałaganiarskie - ale jest to możliwe! Rozwiązaniem jest konsolidacja skryptu w to, co make rozważy pojedynczą komendę podrzędną (pojedynczą linię).
Wskazówki dotyczące pisania skryptów powłoki w plikach makefile:
$
zastępując go$$
;
między poleceniami\
set -e
aby dopasować przepis make do przerwania w przypadku niepowodzenia polecenia podrzędnego()
lub w{}
celu podkreślenia spójności sekwencji wielu linii - że nie jest to typowa sekwencja poleceń makefileOto przykład inspirowany OP:
źródło
SHELL := /bin/bash
w swoim pliku makefile włączyć funkcje specyficzne dla BASH, takie jak zastępowanie procesów .{
ma kluczowe znaczenie, aby zapobiec interpretacji{set
jako nieznanego polecenia.{}
i()
robi dużą różnicę, jeśli czasami chcesz skopiować skrypt i uruchomić go bezpośrednio z zachęty powłoki. Możesz siać spustoszenie w swojej instancji powłoki, deklarując zmienne, a zwłaszcza modyfikując stan za pomocąset
, wewnątrz{}
.()
zapobiega modyfikowaniu środowiska przez skrypt, co jest prawdopodobnie preferowane. Przykład ( będzie to zakończyć sesję powłoki ){ set -e ; } ; false
.command ; ## my comment \` (the comment is between
:; `i` \ `). Wydaje się, że działa dobrze, z wyjątkiem tego , że jeśli uruchomisz polecenie ręcznie (przez kopiowanie i wklejanie), historia polecenia będzie zawierać komentarz w sposób, który łamie polecenie (jeśli spróbujesz go ponownie użyć). [Uwaga: podświetlanie składni w tym komentarzu jest zepsute ze względu na użycie odwrotnego ukośnika wewnątrz odwrotnego znaku]Co jest złego w zwykłym wywoływaniu poleceń?
A jeśli chodzi o drugie pytanie, musisz uciec
$
od$$
znaku, używając zamiast tego, tjbash -c '... echo $$a ...'
.EDYCJA: Twój przykład można przepisać na skrypt jednowierszowy w następujący sposób:
źródło
Oczywiście właściwym sposobem napisania Makefile jest faktyczne udokumentowanie, które cele zależą od źródeł. W trywialnym przypadku proponowane rozwiązanie będzie
foo
zależało od siebie, ale oczywiściemake
jest na tyle inteligentne, aby porzucić zależność cykliczną. Ale jeśli dodasz plik tymczasowy do swojego katalogu, stanie się on „magicznie” częścią łańcucha zależności. Lepiej jest raz na zawsze utworzyć jawną listę zależności, na przykład za pomocą skryptu.GNU make wie, jak uruchomić,
gcc
aby stworzyć plik wykonywalny z zestawu plików.c
.h
plików i , więc może wszystko, czego naprawdę potrzebujesz, toźródło
Dyrektywa ONESHELL pozwala na napisanie wielu receptur wierszy do wykonania w tym samym wywołaniu powłoki.
Jest jednak pewna wada: specjalne prefiksy („@”, „-” i „+”) są interpretowane inaczej.
https://www.gnu.org/software/make/manual/html_node/One-Shell.html
źródło