“Koşmak” için argümanlar geçirmek


354

Makefiles kullanıyorum.

runOluşturma hedefi çalıştıran adlı bir hedef var . Basitleştirilmiş, aşağıdaki gibi görünüyor:

prog: ....
  ...

run: prog
  ./prog

Argümanları iletmenin bir yolu var mı? Böylece

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

Teşekkürler!


Yanıtlar:


264

Tam olarak ne istediğinizi yapmanın bir yolunu bilmiyorum, ancak bir geçici çözüm olabilir:

run: ./prog
    ./prog $(ARGS)

Sonra:

make ARGS="asdf" run
# or
make run ARGS="asdf"

27
@Rob: $ () daha taşınabilir, Nmake'de olduğu gibi çalışır.
John Knoeller

7
@Rob: Nmake, makro genişletme için $ {} 'ı hiç desteklemedi ve şu anda eski bir form gibi görünüyor. $ () baktığım her çevrimiçi eğitici tarafından öneriliyor. $ () ayrıca bash gibi diğer araçlarla daha tutarlıdır.
John Knoeller

10
Belki arkaiktir. Ben her zaman $ {} kullandım, ancak GNU Make devletler için kılavuz "Bir değişkenin değerini değiştirmek için parantez veya parantez içinde değişkenin adının ardından bir dolar işareti yazın: $(foo)' or $ {foo} 'için geçerli bir referanstır değişken "foo". ve yalnızca $ () kullanılan örnekleri vermeye devam eder. Ah güzel.
Jakob Borg

7
John ve sakin tezahürat, geri döndü ve öneri ilk baskı OReilly "Make ile Projeleri Yönetme" kitabımın kopyasından geldiğini gördüm. Yazar, () 'ı ve her ikisini de yapabilen makroları kullanarak arşiv ikamesi kuralını belirtir ancak ayırt etmek için {}' leri kullanmanızı önerir. Ama .... Yeni baskı şimdi "GNU Make ile Projeleri Yönetmek" olarak yeniden adlandırıldı. Git şekil .... Sanırım modernleşmem gerekecek! (-: Yine de MS NMake'nin {} 'de barfs olmasına şaşırdım
Rob Wells

1
@xealits Kesinlikle - burada soruda bir örnek var
helvete

199

Bu soru neredeyse üç yaşında, ama her neyse ...

GNU marka kullanıyorsanız, bunu yapmak kolaydır. Tek sorun, makekomut satırındaki seçenek olmayan bağımsız değişkenleri hedef olarak yorumlayacağıdır. Çözüm, onları hiçbir şey yapma hedefine dönüştürmektir, bu yüzden makeşikayet etmeyin:

# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "run"
  RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(RUN_ARGS):;@:)
endif

prog: # ...
    # ...

.PHONY: run
run : prog
    @echo prog $(RUN_ARGS)

Bunu çalıştırmak:

$ make run foo bar baz
prog foo bar baz

2
Bu harika bir çizgi dışında başlayan argümanlar için işe yaramaz gibi görünüyor:prog foo bar --baz
ingydotnet

22
Çok bu durumda çalışır, ancak söylemek zorunda makeyorumlamak değil --bazbir komut satırı seçeneği olarak: make -- prog foo bar --baz. --Vasıta "Bundan sonra her şey bir argüman, bir seçenek değil".
Idelic

Bunu RUN_ARGSkullanmak için varsayılan bir değeri nasıl tanımlayabilirim ?
Bouke

Belki bir elseşube ekleyin ifeqve RUN_ARGSorada ayarlayın ?
Idelic

1
İyi nokta blueyed! Ancak bunun için bir çözüm var: 'eval' satırını $(eval $(RUN_ARGS):dummy;@:), kukla bir hedef tanımlanmadan değiştirin.
Lucas Cimon

52

standart marka için bunun gibi makrolar tanımlayarak argümanlar iletebilirsiniz

make run arg1=asdf

o zaman onları böyle kullan

run: ./prog $(arg1)
   etc

Microsoft'un NMake'i yapmak için referanslar


34

Değişkeni Makefile'ye aşağıdaki gibi aktarabilirsiniz:

run:
    @echo ./prog $$FOO

Kullanımı:

$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat

veya:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat

Alternatif olarak Beta tarafından sağlanan çözümü kullanın :

run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:

%:- herhangi bir görev adıyla eşleşen kural; @:- boş tarif = hiçbir şey yapma

Kullanımı:

$ make run the dog kicked the cat
./prog the dog kicked the cat

23

TL; DR bunu yapmaya çalışma

$ make run arg

bunun yerine komut dosyası oluşturun:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

ve bunu yapın:

$ ./buildandrunprog.sh arg

belirtilen soruya cevap:

tarifte bir değişken kullanabilirsiniz

run: prog
    ./prog $(var)

sonra değişken atamayı bağımsız değişken olarak

$ make run var=arg

bu yürütülür ./prog arg.

ama tuzaklardan sakının. bu yöntemin tuzaklarını ve diğer yöntemleri daha ayrıntılı olarak açıklayacağım.


sorunun arkasındaki varsayılan niyetin cevabı:

varsayım: progbazı argümanlarla çalıştırmak istiyorsunuz, ancak gerekirse çalıştırmadan önce yeniden yapılandırılmasını istiyorsunuz.

cevap: gerekirse yeniden inşa eden bir komut dosyası oluşturun

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

bu senaryo niyeti çok açık hale getiriyor. iyi yapmak için make'i kullanır: bina. toplu işlem için iyi olanı yapmak için bir kabuk betiği kullanır.

ayrıca, bir kabuk dosyasının tüm esnekliği ve ifadesi ile bir makefile'in tüm uyarıları olmadan ihtiyacınız olan her şeyi yapabilirsiniz.

ayrıca çağıran sözdizimi artık pratik olarak aynıdır:

$ ./buildandrunprog.sh foo "bar baz"

karşılaştırmak:

$ ./prog foo "bar baz"

aksine

$ make run var="foo bar\ baz"

arka fon:

make argümanları bir hedefe aktarmak için tasarlanmamıştır. komut satırındaki tüm bağımsız değişkenler, hedef (hedef olarak da bilinir), seçenek olarak veya değişken ataması olarak yorumlanır.

yani bunu çalıştırırsanız:

$ make run foo --wat var=arg

Marka yorumlayacak runve foogüncellemeye hedefleri (hedefler) olarak tariflere göre. --watyapmak için bir seçenek olarak. ve var=argdeğişken atama olarak.

daha fazla ayrıntı için bkz. https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

terminoloji için bakınız: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


değişken atama yöntemi ve neden buna karşı öneriyorum

$ make run var=arg

ve tarifteki değişken

run: prog
    ./prog $(var)

bu, bir reçete argümanları iletmenin en "doğru" ve en kolay yoludur. ama yapabilirken bağımsız değişken içeren bir programı çalıştırmak için kullanılacak kesinlikle şekilde kullanılmak üzere tasarlanmamıştır. bkz. https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

bence bunun büyük bir dezavantajı var: yapmak istediğiniz şey progargümanla yürütülmek arg. ama yazmak yerine:

$ ./prog arg

yazıyorsun:

$ make run var=arg

boşluk içeren birden çok argümanı veya argümanı iletmeye çalışırken bu daha da zorlaşır:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

karşılaştırmak:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz

kayıt için bu benim proggibi görünüyor:

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done

Ayrıca $(var), makefile içinde tırnak işaretleri koymamalısınız:

run: prog
    ./prog "$(var)"

çünkü o progzaman her zaman sadece bir argüman elde edilir:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz

tüm bu yüzden bu rotaya karşı öneriyorum.


tamlık için burada "çalıştırmak için argüman geçirmek" için başka yöntemler vardır.

Yöntem 1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

süper kısa açıklama: mevcut hedefi hedefler listesinden filtreleyin. %Diğer hedefleri sessizce göz ardı etmek için hiçbir şey yapmayan tüm hedefi yakala ( ) oluştur .

yöntem 2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

süper kısa açıklama: Eğer hedef daha runsonra ilk hedefi kaldırmak ve oluşturmak için kalan hedefler için hiçbir şey yapmayın eval.

her ikisi de böyle bir şey yazmanıza izin verecek

$ make run arg1 arg2

daha derin bir açıklama çalışması için make kılavuzu: https://www.gnu.org/software/make/manual/html_node/index.html

yöntem 1 sorunları:

  • kısa çizgi ile başlayan argümanlar marka haline getirilir ve hedef olarak iletilmez.

    $ make run --foo --bar
    

    geçici çözüm

    $ make run -- --foo --bar
    
  • eşit işaretli argümanlar marka haline getirilir ve geçilmez.

    $ make run foo=bar
    

    geçici çözüm yok

  • boşluklu tartışmalar garip

    $ make run foo "bar\ baz"
    

    geçici çözüm yok

  • bir argüman run(hedefe eşit) olursa, o da kaldırılır

    $ make run foo bar run
    

    ./prog foo baryerine koşacak./prog foo bar run

    yöntem 2 ile geçici çözüm mümkün

  • eğer bir argüman meşru bir hedefse, o da çalıştırılacaktır.

    $ make run foo bar clean
    

    ./prog foo bar cleanaynı zamanda hedefin tarifini de çalıştıracaktır clean(var olduğu varsayılarak).

    yöntem 2 ile geçici çözüm mümkün

  • meşru bir hedefi yanlış yazdığınızda, tüm hedefi yakalama nedeniyle sessizce yok sayılır.

    $ make celan
    

    sessizce görmezden gelir celan .

    geçici çözüm her şeyi ayrıntılı yapmaktır. böylece ne olduğunu görüyorsunuz. ancak bu, meşru çıktı için çok fazla gürültü yaratır.

yöntem 2 sorunları:

  • bir argüman mevcut bir hedefle aynı ada sahipse, make üzerine yazıldığına dair bir uyarı yazdırır.

    bildiğim bir çözüm yok

  • eşit işaretli argümanlar marka haline getirilip yorumlanmayacak

    geçici çözüm yok

  • boşluklarla tartışmalar hala garip

    geçici çözüm yok

  • boşluk aralarıyla argümanlar eval kırmaya çalışan hiçbir şey yapmazlar.

    geçici çözüm: yukarıdaki gibi hiçbir şey yapmadan tüm hedefi yakalamak. yukarıdaki sorunla, yanlış bir şekilde meşru hedefleri sessizce görmezden gelecektir.

  • evalçalışma zamanında makefile değiştirmek için kullanır . okunabilirlik ve hata ayıklama ve en az şaşkınlık ilkesi açısından ne kadar kötüye gidebilirsiniz .

    geçici çözüm: bunu yapmayın !! 1 yerine çalışan ve sonra çalışan bir kabuk komut dosyası yazın prog.

Ben sadece gnu marka kullanarak test ettik. diğer markaların davranışları farklı olabilir.


TL; DR bunu yapmaya çalışma

$ make run arg

bunun yerine komut dosyası oluşturun:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

ve bunu yapın:

$ ./buildandrunprog.sh arg

12

İşte bu kullanım durumlarından bazılarında yardımcı olabilecek başka bir çözüm:

test-%:
    $(PYTHON) run-tests.py $@

Başka bir deyişle, bazı önek seçin ( test-bu durumda) ve ardından hedef adı doğrudan programa / koşucusuna iletin. Hedef programın altında yatan program için yararlı bir şey açabilirsiniz dahil bazı koşucu komut dosyası varsa, bu çoğunlukla yararlı olduğunu düşünüyorum.


2
$*Hedefin yalnızca ile eşleşen kısmını da iletmek için kullanabilirsiniz %.
Malvineous

9

Hayır. GNU markası için man sayfasındaki sözdizimine bakmak

make [-f makefile] [seçenekler] ... [hedefler] ...

birden fazla hedef belirtebilirsiniz, dolayısıyla 'hayır' (en azından tam olarak belirttiğiniz şekilde hayır).


4

Komut satırındaki her n-th bağımsız değişkenini açıkça ayıklayabilirsiniz. Bunu yapmak için, MAKECMDGOALS değişkenini kullanabilirsiniz, hedeflerin bir listesi olarak yorumladığı 'make'e verilen komut satırı argümanlarının listesini tutar. N. İnci bağımsız değişkeni ayıklamak istiyorsanız, bu değişkeni "word" işleviyle birlikte kullanabilirsiniz; örneğin, ikinci bağımsız değişkeni istiyorsanız, aşağıdaki gibi bir değişkende saklayabilirsiniz:

second_argument := $(word 2, $(MAKECMDGOALS) )

Bu, bu bağımsız değişken için make komutunu da çalıştırır. make: *** No rule to make target 'arg'. Stop.
ThomasReggi

2

anon , run: ./progbiraz garip görünüyor, çünkü sağ kısım bir hedef olmalı, yanirun: prog daha iyi görünüyor.

Basitçe öneririm:

.PHONY: run

run:
        prog $(arg1)

ve şunu eklemek istiyorum, bu argümanlar iletilebilir:

  1. argüman olarak: make arg1="asdf" run
  2. veya çevre olarak tanımlanabilir: arg1="asdf" make run

2

İşte benim örneğim. Dev-Cpp ile gelen mingw32-make.exe kullanarak Windows 7 altında yazıyorum unutmayın. (C: \ Windows \ System32 \ make.bat'ım var, bu yüzden komut hala "make" olarak adlandırılıyor.)

clean:
    $(RM) $(OBJ) $(BIN) 
    @echo off
    if "${backup}" NEQ "" ( mkdir ${backup} 2> nul && copy * ${backup} )

Düzenli temizlik için kullanım:

make clean

MyDir'de temizlik ve yedekleme oluşturmak için kullanım /:

make clean backup=mydir

2

Bununla gurur duymuyorum, ama çevre değişkenlerini geçmek istemedim, bu yüzden bir konserve komutunu çalıştırmak için yolu ters çevirdim:

run:
    @echo command-you-want

bu, çalıştırmak istediğiniz komutu yazdıracaktır, bu yüzden bir alt kabukta değerlendirin:

$(make run) args to my command

4
iki yıl sonra bu cevaba baktığımda - neden çevre değişkenlerini kullanmak istemediğimi bu kadar inatçıyordum ve neden başka bir komutun oluşturulmasını satırlaştırmanın daha iyi olduğunu düşündüm?
Conrad.Dean

0

Ben eşittir işaretiyle (=) argüman almak için bir yol buldum! Cevap özellikle @lesmana'nın cevabına bir ektir (burada en eksiksiz ve açıklanmış olanıdır), ancak yorum olarak yazmak çok büyük olurdu. Yine mesajını tekrarlıyorum: TL; DR bunu yapmaya çalışma!

Benim argümanı tedavi etmek için bir yola ihtiyacım vardı --xyz-enabled=false(varsayılan doğru olduğu için), hepimiz biliyoruz ki bu bir hedef değil ve bu nedenle$(MAKECMDGOALS) .

Yankı yaparak yapmak tüm değişkenleri ararken $(.VARIABLES)ben bu ilginç çıktılar var:

[...] -*-command-variables-*- --xyz-enabled [...]

Bu, iki yoldan gitmemize izin verir: ya bir başlangıç --(durumunuz için geçerliyse) ya da GNU'ya özel (muhtemelen bizim kullanmamız için tasarlanmamıştır) değişken ile başlayalım -*-command-variables-*-. ** Ek seçenekler için altbilgiye bakın ** Benim durumumda bu değişken tuttu:

--xyz-enabled=false

Bu değişken ile onu mevcut çözümle birleştirebilir $(MAKECMDGOALS)ve böylece aşağıdakileri tanımlayabiliriz:

# the other technique to invalidate other targets is still required, see linked post
run:
    @echo ./prog $(-*-command-variables-*-) $(filter-out $@,$(MAKECMDGOALS))`

ve bunu kullanarak (açıkça argümanların sırasını karıştırmak):

make run -- config --xyz-enabled=false over=9000 --foo=bar show  isit=alwaysreversed? --help

iade:

./prog isit=alwaysreversed? --foo=bar over=9000 --xyz-enabled=false config show --help

Gördüğünüz gibi, argümanların toplam sırasını kaybediyoruz. "Atama" -args olan bölüm tersine çevrilmiş gibi görünüyor, "hedef" -args sırası tutulur. Başta "atama" -args koydum, umarım programınız argümanın nereye yerleştirildiğini umursamaz.


Güncelleme: aşağıdaki değişkenler de umut verici görünüyor:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false

-2

Kullandığım bir başka numara da kuru bir koşu yapmasını -nsöyleyen bayrak make. Örneğin,

$ make install -n 
# Outputs the string: helm install stable/airflow --name airflow -f values.yaml
$ eval $(make install -n) --dry-run --debug
# Runs: helm install stable/airflow --name airflow -f values.yaml --dry-run --debug
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.