Makefileにループを書き込む方法は?


217

次のコマンドを実行したい:

./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on

これをループとして書く方法はMakefile


同様ですが、少し一般的です:makefileの
nobar

回答:


273

以下は、私がを使用していることから推測できるように./a.out、UNIXタイプのプラットフォームを使用している場合に実行されます。

for number in 1 2 3 4 ; do \
    ./a.out $$number ; \
done

次のようにテストします。

target:
    for number in 1 2 3 4 ; do \
        echo $$number ; \
    done

生成する:

1
2
3
4

範囲が広い場合は、次を使用します。

target:
    number=1 ; while [[ $$number -le 10 ]] ; do \
        echo $$number ; \
        ((number = number + 1)) ; \
    done

これにより、1から10までが出力さwhileれます。コメントに示されているように、はるかに広い範囲で終了条件を10から1000に変更するだけです。

ネストされたループはこうして行うことができます:

target:
    num1=1 ; while [[ $$num1 -le 4 ]] ; do \
        num2=1 ; while [[ $$num2 -le 3 ]] ; do \
            echo $$num1 $$num2 ; \
            ((num2 = num2 + 1)) ; \
        done ; \
        ((num1 = num1 + 1)) ; \
    done

生産:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3

1
qwertは、実際のファイルである可能性が低い、単なるターゲット名です。Makefileルールにはターゲットが必要です。構文エラーについては、いくつか不足しています-アップデートを参照してください。
paxdiablo 2009

2
あなたは彼のシェルが((...))を認識すると仮定しているので、((i = 0; i <WHATEVER; ++ i));のはるかに単純なものを使用しないでください。行う ...; できた?
Idelic 2009

1
注意seq番号のシーケンスを生成するコマンドは、ほとんど(すべて?)のUNIXシステム上に存在する、あなたが書くことができるようにfor number in ``seq 1 1000``; do echo $$number; doneない2、(配列コマンドの両側に単一バッククォートを入れて、私はこれを正しくフォーマットする方法がわかりませんstackoverflowの構文を使用)
スザンヌデュペロン2013

3
おかげで、forループ変数を参照するための二重の$$がありませんでした。
Leif Gruenwoldt 2013年

1
@Jonz:ので、行継続文字がmake正常に実行するためのものとして、各ラインを扱う別のサブシェル。継続しfor number in 1 2 3 4 ; doないと、ループの残りを使わずに、(たとえば)だけでサブシェルを実行しようとします。これにより、実質的にの形式の1行になりwhile something ; do something ; done、完全なステートメントになります。$$質問はここで回答されていますstackoverflow.com/questions/26564825/...を、一見この非常に答えから抽出されたコード:-)で
paxdiablo

265

GNU makeを使用している場合は、

数= 1 2 3 4
やれ:
        $(foreach var、$(NUMBERS)、。/ a.out $(var);)

生成して実行します

./a.out 1; ./a.out 2; ./a.out 3; ./a.out 4;

27
この答えはIMHOの方が優れています。シェルを使用する必要がないため、純粋なmakefileです(たとえGNU固有であっても)。
Jocelyn delalande

7
セミコロンは重要です。それ以外の場合は最初の反復のみが実行されます
ジェレミーライプチヒ

7
このソリューションは、の終了コードを非表示にします./a.out 1./a.out 2関係なく実行されます。
bobbogo 2013

1
@Alexander:あなたが言及している制限について詳しく教えてください。
Idelic 2014年

1
@Idelic、はい。次のメイクファイルとシェルスクリプトの場合、シェルスクリプトは機能します(引数をlsに渡します)が、メイクファイルはエラーになります:make:execvp:/ bin / sh:引数リストが長すぎます Makefile:ix.io/d5L Shellscript:ix.io / d5M これは、異常に長いファイル名のリスト(長いパスも含む)がコマンドに渡されたときに、職場で遭遇したforeach関数の問題です。回避策を見つける必要がありました。
アレクサンダー

107

メイク私見を使用する主な理由はある-jフラグ。make -j5一度に5つのシェルコマンドを実行します。これは、CPUが4つある場合に適しています。また、メイクファイルのテストも適切です。

基本的には、次のようなものを表示したいとします。

.PHONY: all
all: job1 job2 job3

.PHONY: job1
job1: ; ./a.out 1

.PHONY: job2
job2: ; ./a.out 2

.PHONY: job3
job3: ; ./a.out 3

これは-j友好的です(良い兆候)。ボイラープレートを見つけられますか?次のように書くことができます:

.PHONY: all job1 job2 job3
all: job1 job2 job3
job1 job2 job3: job%:
    ./a.out $*

同じ効果のため(はい、これはmakeに関する限り、以前の定式化と同じですが、少しコンパクトです)。

コマンドラインで制限を指定できるようにするためのパラメーター化のさらなるビット(make適切な算術マクロがないので面倒なので、ここでカンニングして使用します$(shell ...)

LAST := 1000
NUMBERS := $(shell seq 1 ${LAST})
JOBS := $(addprefix job,${NUMBERS})
.PHONY: all ${JOBS}
all: ${JOBS} ; echo "$@ success"
${JOBS}: job%: ; ./a.out $*

これをmake -j5 LAST=550で実行しLASTます。デフォルトは1000です。


7
bobbogoが言及した(-j)ため、これが間違いなく最良の答えです。
dbn 2012年

これが.PHONYサフィックスを使用する特別な理由は何ですか?それらは何かに必要ですか?
セブ

6
@seb:なしでは.PHONY: all、makeはというファイルを探しますall。そのようなファイルが存在する場合、makeは次にそのファイルの最終変更時刻をチェックします。メイクファイルはほぼ確実に意図したとおりに動作しません。.PHONY宣言は、make告げるall象徴的なターゲットですが。したがって、Makeはallターゲットが常に古くなっていると見なします。完璧です。マニュアルを参照してください。
bobbogo

4
この行はどのように機能しますか:$ {JOBS}:job%:2番目のセミコロンは何ですか?gnu.org/software/make/manual/make.htmに
Joe

2
@JoeS gnu.org/software/make/manual/make.html#Static-Patternの2番目の*コロン*とは何かを意味している思います)。それらをun-nice(IMHO)パターン規則と混同しないでください。静的パターンルールは、ターゲットのリストがmakeのいかがわしいパターンの1つと一致する場合に非常に役立ちます(依存関係にも同じことが当てはまります)。ここで%は、ルールと一致するものはすべて$*レシピのように使用できるという便宜のために使用します。
bobbogo 2014年

22

私は質問が数年前のものであることを理解していますが、この投稿は上記とは異なるアプローチを示し、シェル操作にも依存せず、開発者がハードコードされたコードを削除する必要もないので、誰かに役立つかもしれません数値の文字列。

$(eval ....)組み込みマクロはあなたの友達です。または少なくともすることができます。

define ITERATE
$(eval ITERATE_COUNT :=)\
$(if $(filter ${1},0),,\
  $(call ITERATE_DO,${1},${2})\
)
endef

define ITERATE_DO
$(if $(word ${1}, ${ITERATE_COUNT}),,\
  $(eval ITERATE_COUNT+=.)\
  $(info ${2} $(words ${ITERATE_COUNT}))\
  $(call ITERATE_DO,${1},${2})\
)
endef

default:
  $(call ITERATE,5,somecmd)
  $(call ITERATE,0,nocmd)
  $(info $(call ITERATE,8,someothercmd)

これは単純な例です。大きな値の場合は適切にスケーリングされません-機能しますが、ITERATE_COUNT文字列は反復ごとに2文字(スペースとドット)ずつ増加するため、数千に達すると、単語を数えるのに徐々に時間がかかります。書かれているように、それはネストされた反復を処理しません(そうするには、別個の反復関数とカウンターが必要になります)。これは純粋にgnu makeであり、シェル要件はありません(もちろん、OPは毎回プログラムを実行しようとしていましたが、ここでは単にメッセージを表示しています)。$(word ...)はエラーになるため、ITERATE内のifは値0をキャッチすることを目的としています。

$(words ...)ビルトインはアラビア語のカウントを提供できるため、カウンターとして機能する成長する文字列が使用されますが、makeはそれ以外の場合は数学演算をサポートしません(1 + 1を何かに割り当てて2を取得することはできません)シェルから何かを呼び出してそれを実行する場合、または同等に複雑なマクロ操作を使用する場合を除きます)。これはINCREMENTALカウンターではうまく機能しますが、DECREMENTカウンターではうまく機能しません。

私自身はこれを使用していませんが、最近、再帰関数を作成して、マルチバイナリ、マルチライブラリのビルド環境全体でライブラリの依存関係を評価する必要がありました。それ自体には他の依存関係(一部はビルドパラメーターによって異なる)があり、上記と同様に$(eval)とカウンターメソッドを使用します(私の場合、カウンターは、どういうわけか無限に進まないようにするために使用されますループ、およびどれだけの反復が必要であったかを報告する診断としても)。

OPのQにとって重要ではないが、他に価値のある何か:$(eval ...)は、循環参照に対するmakeの内部の嫌悪を回避するメソッドを提供します。これは、変数がマクロタイプ( =)、対即時割り当て(:=で初期化)。独自の割り当て内で変数を使用できるようにしたい場合があります。$(eval ...)を使用すると、それを実行できます。ここで考慮すべき重要なことは、evalを実行すると、変数が解決され、解決された部分はマクロとして扱われなくなることです。あなたが何をしているのかを知っていて、自分自身への割り当てのRHSで変数を使用しようとしている場合、これは通常、とにかく実行したいことです。

  SOMESTRING = foo

  # will error.  Comment out and re-run
  SOMESTRING = pre-${SOMESTRING}

  # works
  $(eval SOMESTRING = pre${SOMESTRING}

default:
  @echo ${SOMESTRING}

ハッピーメイク。


20

クロスプラットフォームサポートの場合、コマンドセパレーター(同じ行で複数のコマンドを実行するため)を構成可能にします。

たとえば、WindowsプラットフォームでMinGWを使用している場合、コマンドセパレータは&次のようになります。

NUMBERS = 1 2 3 4
CMDSEP = &
doit:
    $(foreach number,$(NUMBERS),./a.out $(number) $(CMDSEP))

これにより、連結されたコマンドが1行で実行されます。

./a.out 1 & ./a.out 2 & ./a.out 3 & ./a.out 4 &

他で言及されているように、* nixプラットフォームではを使用しますCMDSEP = ;


7

これは質問に対する純粋な答えではありませんが、そのような問題を回避するインテリジェントな方法です。

複雑なファイルを作成する代わりに、たとえばmakefileのようなbashスクリプトに制御を委任するだけです。

foo : bar.cpp baz.h
    bash script.sh

そしてscript.shは次のようになります:

for number in 1 2 3 4
do
    ./a.out $number
done

Makefileを作成しているときに、あるステップで行き詰まりました。次のコードがあります:set_var:@ NUM = 0; while [[$$ NUM <1]]; 「私はここにいます」とエコーします。\ echo $$ NUM dump $$ {NUM} .txt; \ var = "SSA_CORE $$ {NUM} _MAINEXEC"; \ echo $$ var; \ var1 = eval echo \$${$(var)}; \ echo $$ var1; \((NUM = NUM​​ + 1)); \ done all:set_var here SSA_CORE0_MAINEXECはすでに設定されている環境変数です。そのため、変数var1を使用してその値を評価または出力してもらいたいです。上記のように試してみましたが、うまくいきませんでした。助けてください。
XYZ_Linux 2014年

2
これは確かに簡単な回避策ですが、プロセスを並行して実行するための "make -j 4"オプションを使用できなくなります。
TabeaKischka

1
@TabeaKischka:確かに。これは「推奨される」方法ではありませんでしたが、Makefileで提供されていない機能が必要な場合は、実装にフォールバックしてbashその機能を使用できます。ループは、これを実証できる機能の1つです。
Willem Van Onsem


3

set -eforループのプレフィックスとして使用できます。例:

all:
    set -e; for a in 1 2 3; do /bin/false; echo $$a; done

make終了コードですぐに終了します<> 0


0

GNUmakeテーブルツールキットは、真の持っているwhileループを必要とされるものは、反復リストがある場合(実行のその二、三相を有するGNUmakeプログラミングの手段は何でも)、と簡単な解決策がありますinterval。それを楽しむために、数値を16進数に変換します。

include gmtt/gmtt.mk

# generate a list of 20 numbers, starting at 3 with an increment of 5
NUMBER_LIST := $(call interval,3,20,5)

# convert the numbers in hexadecimal (0x0 as first operand forces arithmetic result to hex) and strip '0x'
NUMBER_LIST_IN_HEX := $(foreach n,$(NUMBER_LIST),$(call lstrip,$(call add,0x0,$(n)),0x))

# finally create the filenames with a simple patsubst
FILE_LIST := $(patsubst %,./a%.out,$(NUMBER_LIST_IN_HEX))

$(info $(FILE_LIST))

出力:

./a3.out ./a8.out ./ad.out ./a12.out ./a17.out ./a1c.out ./a21.out ./a26.out ./a2b.out ./a30.out ./a35.out ./a3a.out ./a3f.out ./a44.out ./a49.out ./a4e.out ./a53.out ./a58.out ./a5d.out ./a62.out

0

単純な、シェル/プラットフォームに依存しない、純粋なマクロソリューションは...

%sequence = $(if $(word ${1},${2}),$(wordlist 1,${1},${2}),$(call %sequence,${1},${2} $(words _ ${2})))

$(foreach i,$(call %sequence,10),$(info ./a.out ${i}))

-1
#I have a bunch of files that follow the naming convention
#soxfile1  soxfile1.o  soxfile1.sh   soxfile1.ini soxfile1.txt soxfile1.err
#soxfile2  soxfile2.o   soxfile2.sh  soxfile2.ini soxfile2.txt soxfile2.err
#sox...        ....        .....         ....         ....        ....
#in the makefile, only select the soxfile1.. soxfile2... to install dir
#My GNU makefile solution follows:
tgt=/usr/local/bin/          #need to use sudo
tgt2=/backup/myapplication/  #regular backup 

install:
        for var in $$(ls -f sox* | grep -v '\.' ) ; \
        do \
                sudo  cp -f $$var ${TGT} ;     \
                      cp -f  $$var ${TGT2} ;  \
        done


#The ls command selects all the soxfile* including the *.something
#The grep command rejects names with a dot in it, leaving  
#My desired executable files in a list. 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.