Makefile에서 쉘 명령을 사용하는 방법


99

ls다른 명령 (예 : echo, rsync) 의 결과를 사용하려고합니다 .

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

그러나 나는 얻는다 :

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

echo $$FILES, echo ${FILES}및을 사용해 보았습니다 echo $(FILES).

답변:


149

와:

FILES = $(shell ls)

아래에 들여 쓰기 all는 빌드 명령입니다. 따라서 이것은 확장 된 $(shell ls)다음 명령 실행을 시도합니다 FILES ....

만약 FILES 있어야하는데 make변수이 변수는 레시피 부, 예를 들면 외부에 할당해야

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

물론 이는 .tgz 파일을 생성하는 명령을 실행 하기 전에FILES "output from ls" 으로 설정 된다는 것을 의미 합니다. ( Kaz가 지적했듯이 변수는 매번 다시 확장되므로 결국에는 .tgz 파일이 포함될 것입니다. 일부 FILES := ...는 효율성 및 / 또는 정확성을 위해이를 피해야합니다. 1 )

만약 FILES셸 변수가되어야하는 설정할 수 있지만 공백없이 따옴표로 묶은 shell-ese로 수행해야합니다.

all:
        FILES="$(shell ls)"

그러나 각 줄은 별도의 셸에서 실행되므로이 ​​변수는 다음 줄까지 유지되지 않으므로 즉시 사용해야합니다.

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

쉘이 확장되기 때문에 이것은 모두 약간 어리석은 일입니다. * (및 기타 셸 glob 표현식) 일입니다.

        echo *

쉘 명령으로.

마지막으로, 일반적인 규칙 (이 예에는 실제로 적용되지 않음) : esperanto가 주석에서 언급했듯이 출력을 사용하는 ls것은 완전히 신뢰할 수 없습니다 (일부 세부 정보는 파일 이름에 따라 다르며 때로는 버전에 따라 다릅니다 ls. 일부 버전의 ls출력 삭제 시도 일부 경우에). 따라서 l0b0이상적인 메모 와 같이 GNU를 사용하는 경우 자체 내부의 모든 것을 사용 $(wildcard)하고 $(subst ...)수행 할 수 있습니다 make( "파일 이름의 이상한 문자"문제 방지). ( sh메이크 파일의 레시피 부분을 포함하는 스크립트에서 또 다른 방법은 find ... -print0 | xargs -0공백, 줄 바꿈, 제어 문자 등이 넘어 가지 않도록하는 것입니다.)


1 GNU Make 문서는 POSIX ::=가 2012 년 에 할당을 추가했다고 추가로 설명 합니다 . 나는 이것에 대한 POSIX 문서에 대한 빠른 참조 링크를 찾지 못했고 어떤 make변형이 ::=할당을 지원 하는지 직접 알지 못합니다 . 비록 GNU make가 오늘과 같은 의미로 :=, 즉 확장과 함께 할당을 수행하지만.

참고 VAR := $(shell command args...)또한 철자 할 수 있습니다 VAR != command args...몇 가지에 make모든 현대적인 GNU를 포함, 변형 및 BSD는 내가 아는 한 변종입니다. 이 다른 변형이없는 $(shell)때문에 사용 VAR != command args...이 모두 짧은있는 우수 하고 더 변종에서 작업.


감사. 정교한 명령 ( 예 : lswith sed및 cut)을 사용한 다음 rsync 및 기타 명령에서 결과 를 사용하고 싶습니다 . 긴 명령을 계속 반복해야합니까? 결과를 내부 Make 변수에 저장할 수 없습니까?
아담 마탄

1
Gnu make는 그렇게 할 수있는 방법을 가지고 있을지 모르지만 저는 그것을 사용한 적이 없습니다. 그리고 우리가 사용하는 끔찍하게 복잡한 makefile은 단지 쉘 변수와 각 줄의 끝에 "; 필요합니다. (흠, 여기에 백 슬래시 순서와 함께 작동하도록 코드 인코딩을받을 수 없습니다)
torek

1
아마도 뭔가 같은 : FILE = $(shell ls *.c | sed -e "s^fun^bun^g")
윌리엄 모리스

2
@William : make쉘을 사용하지 않고도 할 수 있습니다 : FILE = $(subst fun,bun,$(wildcard *.c)).
Idelic

1
이 경우에는 그다지 중요하지 않은 것 같지만 ls의 출력을 자동으로 구문 분석해서는 안된다는 점을 지적하고 싶습니다. ls는 인간에게 정보를 보여주기위한 것이지 스크립트에 묶여 있지 않습니다. 여기에 더 많은 정보 : mywiki.wooledge.org/ParsingLs 아마도 'make'가 적절한 와일드 카드 확장을 제공하지 않는 경우, "find"가 "ls"보다 낫습니다.
Raúl Salinas-Monteagudo

53

또한 torek의 답변 외에도 눈에 띄는 점은 느리게 평가 된 매크로 할당을 사용하고 있다는 것입니다.

당신이 GNU 메이크업에있어 경우, 사용하는 :=대신 할당을 =. 이 할당으로 인해 오른쪽이 즉시 확장되고 왼쪽 변수에 저장됩니다.

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

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

=할당 을 사용하면의 모든 단일 발생이 구문을 $(FILES)확장 $(shell ...)하여 쉘 명령을 호출 함을 의미합니다 . 이렇게하면 make 작업이 느리게 실행되거나 놀라운 결과가 발생할 수 있습니다.


1
이제 목록을 얻었으므로 목록의 각 항목을 어떻게 반복하고 명령을 실행합니까? 빌드 또는 테스트와 같은?
anon58192932

3
@ anon58192932 명령을 실행하기위한 특정 반복은 일반적으로 빌드 레시피의 셸 구문 조각에서 수행되므로 make :가 아닌 셸에서 발생합니다 for x in $(FILES); do command $$x; done. $$싱글 $을 셸로 전달하는 두 배로 늘어납니다 . 또한 쉘 조각은 한 줄입니다. 여러 줄로 된 셸 코드를 작성하려면 make자체적 으로 처리 되고 한 줄로 접히는 백 슬래시 연속을 사용 합니다. 즉, 쉘 명령으로 구분되는 세미콜론은 필수입니다.
Kaz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.