Makefile'da çok satırlı bash komutları


120

Birkaç satırlık bash komutuyla projemi derlemek için çok rahat bir yolum var. Ama şimdi bunu makefile ile derlemem gerekiyor. Her komutun kendi kabuğunda çalıştırıldığını düşünürsek, sorum şu : Birbirine bağlı olarak makefile'da çok satırlı bash komutunu çalıştırmanın en iyi yolu nedir? Örneğin, bunun gibi:

for i in `find`
do
    all="$all $i"
done
gcc $all

Ayrıca, biri tek satırlı komutun neden bash -c 'a=3; echo $a > file'terminalde doğru çalıştığını ancak makefile durumunda boş dosya oluşturduğunu açıklayabilir mi?


4
İkinci bölüm ayrı bir soru olmalıdır. Başka bir soru eklemek sadece gürültü yaratır.
l0b0

Yanıtlar:


136

Satırın devamı için ters eğik çizgi kullanabilirsiniz. Ancak, kabuğun tüm komutu tek bir satırda birleştirilmiş olarak aldığını unutmayın, bu nedenle bazı satırları da noktalı virgülle sonlandırmanız gerekir:

foo:
    for i in `find`;     \
    do                   \
        all="$$all $$i"; \
    done;                \
    gcc $$all

Ancak, yalnızca findçağrı tarafından döndürülen tüm listeyi alıp ona iletmek gccistiyorsanız, aslında çok satırlı bir komuta ihtiyacınız yoktur:

foo:
    gcc `find`

Veya daha geleneksel bir $(command)yaklaşım kullanarak ( $yine de kaçışa dikkat edin ):

foo:
    gcc $$(find)

2
Çok satırlı için, başka yerdeki yorumlarda belirtildiği gibi, yine de dolar işaretlerinden kaçmanız gerekir.
üçlü

1
Evet, kısa bir cevap 1. $: = $$ 2. multiline append \ BTW bash çocuklar genellikle `find` çağrısını $$ (find) ile değiştirmeyi tavsiye ediyor
Yauhen Yakimovich

89

Soruda belirtildiği gibi, her alt komut kendi kabuğunda çalıştırılır . Bu, önemsiz olmayan kabuk komut dosyaları yazmayı biraz karmaşık hale getirir - ama bu mümkündür! Çözüm, betiğinizi markanın tek bir alt komut (tek bir satır) olarak kabul edeceği şekilde birleştirmektir.

Makefiles içinde kabuk betikleri yazmak için ipuçları:

  1. İle $değiştirerek komut dosyasının kullanımından kurtulun$$
  2. ;Komutlar arasına ekleyerek komut dosyasını tek bir satır olarak çalışacak şekilde dönüştürün
  3. Komut dosyasını birden çok satıra yazmak istiyorsanız, satır sonundan kaçının \
  4. İsteğe bağlı olarak set -e, alt komut başarısızlığında iptal etme hükmüyle eşleştirmek için başlayın
  5. Bu tamamen isteğe bağlıdır, ancak betiği köşeli parantez içine sokabilir ()veya {}çok satırlı bir dizinin tutarlılığını vurgulayabilirsiniz - bu tipik bir makefile komut dizisi değildir

İşte OP'den esinlenen bir örnek:

mytarget:
    { \
    set -e ;\
    msg="header:" ;\
    for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
    msg="$$msg :footer" ;\
    echo msg=$$msg ;\
    }

4
SHELL := /bin/bashMakefile'nizde, işlem ikamesi gibi BASH'ye özgü özellikleri etkinleştirmesini de isteyebilirsiniz .
Brent Bradburn

8
İnce nokta: Sonraki boşluk {, {setbilinmeyen bir komut olarak yorumlanmasını önlemek için çok önemlidir .
Brent Bradburn

3
İnce nokta # 2: Makefile'da muhtemelen önemli değildir, ancak bazen komut dosyasını kopyalamak ve doğrudan bir kabuk komut isteminden çalıştırmak istiyorsanız, sarma {}ve sarma arasındaki ()fark büyük bir fark yaratır. Değişkenleri bildirerek ve özellikle setiçinde ile durumu değiştirerek kabuk örneğinize zarar verebilirsiniz {}. ()komut dosyasının ortamınızı değiştirmesini engeller, bu muhtemelen tercih edilir. Örnek ( Bu kabuk oturumu sona erecek ): { set -e ; } ; false.
Brent Bradburn

Aşağıdaki formu kullanarak yorum ekleyebilirsiniz command ; ## my comment \` (the comment is between :; `ve` \ `). Bu hariç çalışma cezası görünüyor ki eğer el komutu çalıştırın (tarafından kopyalama ve yapıştırma), komut geçmişi bir şekilde yorumunu içereceğini sonları komutu (bunu yeniden denerseniz). [Not: Bu yorum için sözdizimi vurgulaması, geri
tepme

Makefile içindeki bash / perl / script içindeki bir dizgeyi kırmak istiyorsanız, string tırnak, ters eğik çizgi, satırsonu, (girintisiz) açık dize alıntılarını kapatın. Misal; perl -e QUOTE baskı 1; QUOTE BACKSLASH NEWLINE QUOTE print 2 QUOTE
mosh

2

Sadece komutları çağırmanın nesi yanlış?

foo:
       echo line1
       echo line2
       ....

Ve ikinci sorunuz için, bunun yerine $kullanarak kaçmanız gerekiyor $$, yani bash -c '... echo $$a ...'.

DÜZENLEME: Örneğiniz aşağıdaki gibi tek satırlık bir komut dosyasına yeniden yazılabilir:

gcc $(for i in `find`; do echo $i; done)

5
Neden "her komut kendi kabuğunda çalıştırılır", oysa ben command2'de command1 sonucunu kullanmam gerekiyor. Örnekte olduğu gibi.
Jofsey

Bilgileri kabuk çağrıları arasında yaymanız gerekirse, geçici bir dosya gibi bir tür harici depolama alanı kullanmanız gerekir. Ancak, aynı kabukta birden çok komut çalıştırmak için her zaman kodunuzu yeniden yazabilirsiniz.
JesperE

+1, özellikle basitleştirilmiş sürüm için. Ve bu sadece "gcc" find "'yapmakla aynı şey değil mi?
Eldar Abusalimov

1
Evet öyle. Ama elbette sadece "yankı" dan daha karmaşık şeyler de yapabilirsiniz.
JesperE

2

Elbette bir Makefile yazmanın doğru yolu, hangi hedeflerin hangi kaynaklara bağlı olduğunu belgelemektir. Önemsiz durumda, önerilen çözüm fookendine bağımlı hale gelecektir , ancak elbette makedöngüsel bir bağımlılığı ortadan kaldıracak kadar akıllıdır. Ancak dizininize geçici bir dosya eklerseniz, "sihirli bir şekilde" bağımlılık zincirinin bir parçası olacaktır. Belki bir komut dosyası aracılığıyla bir kez ve herkes için açık bir bağımlılıklar listesi oluşturmak daha iyidir.

GNU make gccbir dizi .cve .hdosyadan çalıştırılabilir bir dosya üretmek için nasıl çalıştırılacağını bilir , bu yüzden belki de gerçekten ihtiyacınız olan tek şey

foo: $(wildcard *.h) $(wildcard *.c)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.