Makefile'da kabuk komutları nasıl kullanılır


102

Sonuçlarını lsdiğer komutlarda kullanmaya çalışıyorum (örneğin echo, rsync):

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

Ama anlıyorum:

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

Ben kullanarak denedim echo $$FILES, echo ${FILES}ve echo $(FILES)hiçbir şans ile.

Yanıtlar:


157

İle:

FILES = $(shell ls)

Altında allböyle girintili , bu bir inşa komutu. Böylece bu genişler $(shell ls), ardından komutu çalıştırmayı dener FILES ....

Eğer FILESbir olması gerekiyordu makedeğişken, bu değişkenler tarifi kısmı, örneğin dışında atanması gerekir:

FILES = $(shell ls)
all:
        echo $(FILES)

Elbette bu, .tgz dosyalarını oluşturan komutlardan herhangi birini çalıştırmadan önceFILES "çıkış kaynağı ls" olarak ayarlanacağı anlamına gelir . ( Kaz değişkenin her seferinde yeniden genişletildiğini belirtse de, sonunda .tgz dosyalarını içerecektir; bazı üretici varyantlarının verimlilik ve / veya doğruluk için bundan kaçınması gerekir . 1 )FILES := ...

Eğer FILESbir kabuk değişkeni olması gerekiyordu, bunu ayarlayabilirsiniz ancak boşluk olmadan, kabuk-ese bunu yapmak gerekmez ve alıntı:

all:
        FILES="$(shell ls)"

Bununla birlikte, her satır ayrı bir kabuk tarafından çalıştırılır, bu nedenle bu değişken bir sonraki satıra kadar hayatta kalmayacaktır, bu nedenle hemen kullanmanız gerekir:

        FILES="$(shell ls)"; echo $$FILES

*Kabuk sizin için (ve diğer kabuk glob ifadelerini) ilk etapta genişleyeceğinden, bu biraz aptalca , böylece şunları yapabilirsiniz:

        echo *

Kabuk komutunuz olarak.

Son olarak, genel bir kural olarak (bu örnek için gerçekten geçerli değildir): esperanto'nun yorumlarda belirttiği gibi , çıkışını kullanmak lstamamen güvenilir değildir (bazı ayrıntılar dosya adlarına ve hatta bazen sürümüne bağlıdır ls; lsçıktıyı temizleme girişiminin bazı sürümleri bazı durumlarda). Bu nedenle, l0b0 ve idelic not olarak, GNU make'i kullanıyorsanız, kendi içindeki her şeyi başarabilir $(wildcard)ve $(subst ...)başarabilirsiniz make("dosya adındaki garip karakterler" sorunlarından kaçınarak). ( shMakefile dosyalarının tarif bölümü de dahil olmak üzere komut dosyalarında, find ... -print0 | xargs -0boşluklar, satırsonları, kontrol karakterleri vb. Üzerinde gezinmekten kaçınmak için başka bir yöntem kullanılır .)


1 GNU Make belgeleri, POSIX'in ::=2012'de ek atamalar yaptığını belirtmektedir . Bunun için bir POSIX belgesine hızlı bir referans bağlantısı bulamadım ve hangi makevaryantların ::=atamayı desteklediğini de bilmiyorum , ancak bugün GNU yapmakla aynı anlama geliyor :=, yani atamayı şimdi genişletme ile yapıyor.

Bildiğim kadarıyla tüm modern GNU ve BSD varyantları da dahil olmak üzere çeşitli varyantlarda VAR := $(shell command args...)yazılabileceğini unutmayın . Bu diğer varyantlar yok bu yüzden kullanarak hem daha kısa olmak üstündür ve daha varyantları çalışan.VAR != command args...make$(shell)VAR != command args...


Teşekkürler. Ayrıntılı bir komut kullanmak istiyorum ( örneğin lsile sedve kes) ve sonra sonuçları rsync ve diğer komutlarda kullanmak istiyorum. Uzun komutu defalarca tekrar etmem gerekiyor mu? Sonuçları dahili bir Make değişkeninde saklayamaz mıyım?
Adam Matan

1
Gnu make bunu yapmanın bir yolu olabilir, ancak ben onu hiç kullanmadım ve kullandığımız tüm korkunç derecede karmaşık makefile'lar sadece kabuk değişkenlerini ve her satırın sonunda "; \" ile oluşturulan dev tek satırlık kabuk komutlarını kullanır. gerekli. (Evet, burada eğik çizgi dizisi ile iş için kodu kodlamasını alamayan)
torek

1
Belki şöyle bir şey: FILE = $(shell ls *.c | sed -e "s^fun^bun^g")
William Morris

2
@William: makekabuğu kullanmadan yapabilirsiniz: FILE = $(subst fun,bun,$(wildcard *.c)).
Idelic

1
Bu durumda çok önemli görünmese de, ls'nin çıktısını otomatik olarak çözümlememeniz gerektiğini belirtmek isterim. İnsanoğluna bilgi göstermesi amaçlanmıştır, senaryolarla zincirlenmek için değil. Burada daha fazla bilgi: mywiki.wooledge.org/ParsingLs Muhtemelen, 'make' size uygun bir joker karakter genişletmesi sunmuyorsa, "bul" "ls" den daha iyidir.
Raúl Salinas-Monteagudo

53

Ayrıca, Tork'un cevabına ek olarak: göze çarpan bir şey, tembel olarak değerlendirilmiş bir makro ataması kullanıyor olmanızdır.

GNU Make üzerindeyseniz, :=bunun yerine atamayı kullanın =. Bu atama, sağ tarafın hemen genişletilmesine ve sol el değişkeninde saklanmasına neden olur.

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

=Atamayı kullanırsanız , bu, her bir oluşumunun sözdizimini $(FILES)genişleteceği $(shell ...)ve böylece kabuk komutunu çalıştıracağı anlamına gelir. Bu, işinizin daha yavaş ilerlemesine veya hatta bazı şaşırtıcı sonuçlara neden olacaktır.


1
Listeye sahip olduğumuza göre, listedeki her bir öğeyi nasıl yineleyip üzerinde bir komut çalıştırabiliriz? Yapı veya test gibi mi?
anon58192932

3
@ anon58192932 Bu belirli yineleme, bir komutu yürütmek için genellikle bir yapı tarifindeki bir kabuk sözdizimi parçasında yapılır, bu nedenle make: yerine kabukta gerçekleşir for x in $(FILES); do command $$x; done. $$Kabuğa bir single geçen ikiye katlanmış olanı not edin $. Ayrıca, kabuk parçaları tek astarlıdır; çok satırlı kabuk kodu yazmak için, makekendi başına işlenen ve tek satıra katlanan ters eğik çizgi devamını kullanırsınız . Bu, kabuk komutunu ayıran noktalı virgüllerin zorunlu olduğu anlamına gelir.
Kaz
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.