Makefile에서 루프를 작성하는 방법?


217

다음 명령을 실행하고 싶습니다.

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

이 것을 루프에서 루프로 쓰는 방법은 Makefile무엇입니까?


비슷하지만 약간 더 일반적입니다 : makefile의 여러 줄 bash 명령
nobar

답변:


273

을 사용하여 가정 한 것처럼 ./a.outUNIX 유형 플랫폼을 사용하는 경우 다음이 수행 됩니다.

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

2
그의 쉘이 ((...))을 인식한다고 가정하기 때문에 ((i = 0; i <WHATEVER; ++ i))에 대해 더 간단한 것을 사용하지 않는 이유는 무엇입니까? 하다 ...; 완료?
Idelic

1
있습니다 seq당신이 쓸 수 있도록 일련의 숫자는, 대부분의 (모든?) 유닉스 시스템에 존재 생성 명령 for number in ``seq 1 1000``; do echo $$number; done합니다 (서열 명령이 아닌 두 가지의 각 측면에 하나의 역 따옴표를 넣고,이를 제대로 포맷하는 방법을 모른다 stackoverflow의 구문 사용)
Suzanne Dupéron

3
고마워, for 루프 변수를 참조하는 이중 $$가 누락되었습니다.
Leif Gruenwoldt

1
@Jonz : 줄 연속 문자는 make일반적으로 각 줄을 별도의 하위 셸 에서 실행하는 것으로 취급 하기 때문 입니다. 계속하지 않으면 for number in 1 2 3 4 ; do루프의 나머지 부분없이 (예를 들어) 서브 쉘을 실행하려고합니다 . 그것으로, 그것은 효과적으로 while something ; do something ; done완전한 진술인 형식의 한 줄이됩니다 . $$질문은 여기에 대한 답변 : stackoverflow.com/questions/26564825/...을 , 겉으로 :-) 바로이 대답에서 추출한 코드
paxdiablo

265

GNU make를 사용하고 있다면 시도해 볼 수 있습니다.

숫자 = 12 34
해:
        $ (for var, $ (NUMBERS),. / a.out $ (var);)

생성하고 실행할 것입니다

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

27
이 답변은 IMHO가 더 낫습니다. 쉘을 사용할 필요가 없기 때문에 순수한 GNU makefile 인 경우에도 순수한 makefile입니다.
Jocelyn delalande

7
세미콜론 그렇지 않으면 첫 번째 반복 실행됩니다 중요하다
제레미 라이프 치히

7
이 솔루션은의 종료 코드를 숨 깁니다 ./a.out 1. ./a.out 2상관없이 실행됩니다.
bobbogo

1
@Alexander : 당신이 말하는 제한에 대해 자세히 설명해 주시겠습니까?
Idelic

1
@Idelic, 그렇습니다. 다음 makefile 및 셸 스크립트의 경우 셸 스크립트가 작동하고 (인수에 ls를 전달 함) Makefile에 오류가 발생합니다. make : execvp : / bin / sh : 인수 목록이 너무 깁니다. Makefile : ix.io/d5L Shellscript : ix.io / d5M 이것은 비정상적으로 긴 파일 이름 목록 (긴 경로도 포함)이 명령에 전달 될 때 직장에서 마주 친 foreach 함수의 문제입니다. 해결 방법을 찾아야했습니다.
Alexander

107

사용 메이크업 이럴에 대한 주요 이유는이다 -j플래그. make -j5한 번에 5 개의 셸 명령을 실행합니다. CPU가 4 개인 경우, 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좋은 산술 매크로가 없으므로 지루 하므로 여기서 속이고 사용합니다 $(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에, LAST1000 디폴트.


7
bobbogo가 ( -j )를 언급 한 이유 때문에 이것이 가장 좋은 대답 입니다.
dbn

이것이 .PHONY 접미사를 사용하는 특별한 이유는 무엇입니까? 그들은 무엇이든 필요합니까?
Seb

6
@seb :가 없으면 .PHONY: allmake라는 파일을 찾습니다.all . 이러한 파일이 존재하면 make는 해당 파일의 마지막 변경 시간을 확인하며 makefile은 의도 한대로 작동하지 않습니다. .PHONY선언은 메이크업 알려줍니다 all상징적 인 목표입니다. 따라서 Make는 all대상이 항상 오래된 것으로 간주합니다 . 완전한. 설명서를 참조하십시오.
bobbogo

4
이 행의 작동 방식 : $ {JOBS} : job % : 두 번째 세미콜론은 무엇입니까? 나는 아무것도 보지 못했다gnu.org/software/make/manual/make.htm
Joe

2
@JoeS gnu.org/software/make/manual/make.html#Static-Pattern ( 두 번째 * 콜론은 무엇 을 의미한다고 생각합니다 ). 비정규 (IMHO) 패턴 규칙과 혼동하지 마십시오 . 정적 패턴 규칙은 대상 목록이 make 의 noddy 패턴 중 하나와 일치 할 때마다 유용 합니다 (종속성에 동일하게 적용됨). 여기서는 %규칙에서 일치하는 $*것이 레시피에서와 같이 사용할 수 있는 편의를 위해 하나를 사용 합니다.
보보고

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 ...) 내장은 아라비아 숫자를 제공 할 수 있기 때문에 카운터로 사용할 문자열이 증가한다는 점에 유의하십시오. 쉘에서 무언가를 호출하여 그것을 달성하거나 동등하게 복잡한 매크로 작업을 사용하지 않는 한). 이것은 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))

이렇게하면 연결된 명령이 한 줄로 실행됩니다.

./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; [[$$ NUM <1]]; echo "나는 여기 있습니다"; \ 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

2
이것은 실제로 쉬운 해결 방법이지만 프로세스를 병렬로 실행하기 위해 멋진 "make -j 4"옵션을 사용하지 못하게합니다.
TabeaKischka

1
@TabeaKischka : 참으로. " 권장 "방식은 아니지만 Makefile에서 제공하지 않는 일부 기능이 필요한 경우 구현에 대체하여 bash해당 기능을 사용할 수 있습니다. 루프는이를 설명 할 수있는 기능 중 하나입니다.
Willem Van Onsem

4

아마도 당신은 사용할 수 있습니다 :

xxx:
    for i in `seq 1 4`; do ./a.out $$i; done;

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.