258

我想执行以下命令:

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

如何将这个东西写成一个循环Makefile

4

15 回答 15

325

如果您使用./a.out.

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
于 2009-09-29T06:46:33.933 回答
298

如果你使用 GNU make,你可以试试

数字 = 1 2 3 4
做:
        $(foreach var,$(NUMBERS),./a.out $(var);)

这将生成并执行

./a.out 1; ./a.out 2; ./a.out 3; ./a.out 4;
于 2009-09-29T07:06:05.403 回答
128

使用 make 恕我直言的主要原因是-j标志。make -j5将一次运行 5 个 shell 命令。如果你有 4 个 CPU,这很好,并且可以很好地测试任何 makefile。

基本上,您希望看到类似的内容:

.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=550LAST默认为 1000。

于 2012-08-24T13:51:45.450 回答
24

我意识到这个问题已经有好几年了,但是这篇文章可能仍然对某人有用,因为它展示了一种与上述不同的方法,并且不依赖于 shell 操作,也不需要开发人员排除硬编码数值字符串。

$(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,没有 shell 要求(尽管显然 OP 每次都希望运行一个程序——在这里,我只是显示一条消息)。ITERATE 中的 if 旨在捕获值 0,因为 $(word...) 否则会出错。

请注意,使用增长字符串作为计数器是因为 $(words...) 内置函数可以提供阿拉伯语计数,但是 make 不支持数学运算(您不能将 1+1 分配给某物并得到 2,除非您从 shell 调用某些东西来为您完成它,或者使用同样复杂的宏操作)。这对于增量计数器非常有效,但是对于减量计数器则不是很好。

我自己不使用它,但最近,我需要编写一个递归函数来评估跨多二进制、多库构建环境的库依赖关系,当你包含一些库时,你需要知道引入其他库它本身有其他依赖项(其中一些依赖项取决于构建参数),我使用类似于上面的 $(eval) 和 counter 方法(在我的例子中,计数器用于确保我们不会以某种方式进入无休止的状态循环,也可以作为诊断报告需要多少迭代)。

其他东西一文不值,尽管对 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}

制作愉快。

于 2012-11-20T23:34:31.030 回答
22

对于跨平台支持,使命令分隔符(用于在同一行上执行多个命令)可配置。

例如,如果您在 Windows 平台上使用 MinGW,则命令分隔符为&

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

这将在一行中执行连接的命令:

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

如其他地方所述,在 *nix 平台上使用CMDSEP = ;.

于 2012-09-26T15:37:51.550 回答
7

这并不是对问题的真正纯粹答案,而是解决此类问题的明智方法:

无需编写复杂的文件,只需将控制权委托给例如 bash 脚本,例如:makefile

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

和 script.sh 看起来像:

for number in 1 2 3 4
do
    ./a.out $number
done
于 2013-10-11T11:01:27.217 回答
5

也许你可以使用:

xxx:
    for i in `seq 1 4`; do ./a.out $$i; done;
于 2015-02-16T19:44:01.417 回答
3

您可以set -e用作 for 循环的前缀。例子:

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

make将立即退出并带有退出代码<> 0

于 2015-10-01T07:19:12.770 回答
1

尽管GNUmake 表工具包有一个真正的while循环(无论这在 GNUmake 编程中的两个或三个执行阶段意味着什么),但如果需要的是一个迭代列表,那么有一个简单的解决方案interval。为了好玩,我们也将数字转换为十六进制:

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
于 2020-02-13T12:02:18.810 回答
0

一个简单的、独立于外壳/平台的纯宏解决方案是......

# GNU make (`gmake`) compatible; ref: <https://www.gnu.org/software/make/manual>
define EOL

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

.PHONY: target
target:
    $(foreach i,$(call %sequence,10),./a.out ${i}${EOL})
于 2020-05-14T23:11:08.333 回答
0

在循环中动态分配变量

- 解决方案的问题for number in 1 2 3 4 ; do \ ...是,不能在循环内分配任何变量$(eval VAR=...)只能在目标执行开始时知道赋值内容的情况下使用。如果赋值依赖于循环变量,VAR将为

为了规避这个问题,可以使用目标功能对循环进行建模。以下示例从 SRC / OBJ 中获取第 n 个文件并将它们一起处理。使用这种结构,您甚至可以$(eval ...)用来处理循环变量,如VAR3.

生成文件

SRC = f1.c f2.cpp f3.cpp
OBJ = f1.o f2.o f3.o

SRC2 = $(addsuffix _,$(SRC))
JOIN = $(join $(SRC2),$(OBJ))

PHONY: all
all : info loop

loop : $(JOIN)

$(JOIN) :
    @# LOOP - CONTENT
    @echo "TARGET: $@"
    $(eval VAR1=$(word 1,$(subst _, ,$@)))
    @echo "VAR1: "$(VAR1)
    $(eval VAR2=$(word 2,$(subst _, ,$@)))
    @echo "VAR2: "$(VAR2)
    $(eval VAR3=$(subst .o,.x,$(VAR2)))
    @echo "You can even substitute you loop variable VAR3: "$(VAR3)
    #g++ -o $(VAR2) $(VAR1)
    @echo

PHONY: info
info:
    @printf "\n"
    @echo "JOIN: "$(JOIN)
    @printf "\n"

输出

$ make

JOIN: f1.c_f1.o f2.cpp_f2.o f3.cpp_f3.o

TARGET: f1.c_f1.o
VAR1: f1.c
VAR2: f1.o
You can even substitute you loop variable VAR3: f1.x
#g++ -o f1.o f1.c

TARGET: f2.cpp_f2.o
VAR1: f2.cpp
VAR2: f2.o
You can even substitute you loop variable VAR3: f2.x
#g++ -o f2.o f2.cpp

TARGET: f3.cpp_f3.o
VAR1: f3.cpp
VAR2: f3.o
You can even substitute you loop variable VAR3: f3.x
#g++ -o f3.o f3.cpp
于 2020-07-29T16:03:33.257 回答
0

这个答案,就像@Vroomfondel 旨在以优雅的方式规避循环问题一样。

我的想法是让make循环本身作为导入的 makefile 生成,如下所示:

include Loop.mk
Loop.mk:Loop.sh
     Loop.sh > $@

shell 脚本可以随心所欲,但一个最小的工作示例可能是

#!/bin/bash
LoopTargets=""
NoTargest=5
for Target in `seq $NoTargest` ; do
    File="target_${Target}.dat"
    echo $File:data_script.sh
    echo $'\t'./data_script.ss $Target
    LoopTargets="$LoopTargets $File"
done
echo;echo;echo LoopTargets:=$LoopTargets

生成文件

target_1.dat:data_script.sh
    ./data_script.ss 1
target_2.dat:data_script.sh
    ./data_script.ss 2
target_3.dat:data_script.sh
    ./data_script.ss 3
target_4.dat:data_script.sh
    ./data_script.ss 4
target_5.dat:data_script.sh
    ./data_script.ss 5


LoopTargets:= target_1.dat target_2.dat target_3.dat target_4.dat target_5.dat

优点是它make本身可以跟踪哪些文件已经生成,哪些需要(重新)生成。因此,这也允许make使用该-j标志进行并行化。

于 2021-01-19T10:34:03.647 回答
0

这对我有用:

NUM=4

a-out:
    for (( i=1; i<=${NUM}; i++ )) \
    do \
        ./a.out $$i ; \
    done
于 2022-01-09T16:05:30.697 回答
0

版本

代码

files := $(wildcard ./*.txt ./**/*.go */**/*.js )

showFileFunc = echo "$(abspath ${1})\${2}"
delFileFunc = del "$(abspath ${1})\${2}"
cmdSplit = &
targetDisplay:
    $(foreach curFile, ${files}, ${call showFileFunc,${dir $(curFile)},${notdir $(curFile)}} ${cmdSplit})
targetDelete:
    $(foreach curFile, ${files}, ${call delFileFunc,${dir $(curFile)},${notdir $(curFile)}} ${cmdSplit})

测试目录

Makefile
1.txt
 myDir
   - foo.go
   - bar.go
   - subDir
     - qoo.js

测试 CMD 和输出

make showFile -s

output:
"C:/...\1.txt"
"C:/.../myDir\bar.go"
"C:/.../myDir\foo.go"          // since `.//**.js`
"C:/.../myDir/subDir\qoo.js"   // `.//**.js` can't but `*/**/*.js` is OK

参考

于 2022-02-13T11:53:04.757 回答
-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. 
于 2015-05-25T18:18:41.500 回答