다음 명령이 있습니다.
find stdlib/main -type f -exec sh -c "echo {} | sed -e 's/stdlib\/main\///g' -e 's/\.q//g' -e 's/\//\./g' -e 's~^~/resource:\"{},~g' -e 's/$/\"/g'" \;
목표는 stdlib/main
(및 하위 디렉터리)에서 모든 파일을 찾아 다음과 같이 형식을 지정하는 것입니다.{filename},{filename-with-stdlibmain-removed-and-extension-removed-and-slashes-changed-to-dots}
직접 명령을 실행하면 명령이 완벽하게 실행됩니다. 하지만 메이크파일에서 사용하려고 합니다.
STDLIB_RESOURCES=$(shell find stdlib/main -type f -exec sh -c "echo {} | sed -e 's/stdlib\/main\///g' -e 's/\.neo//g' -e 's/\//\./g' -e 's~^~/resource:\"{},~g' -e 's/$/\"/g'" \;)
makefile을 실행하면 발견된 모든 파일에 대해 다음 오류 중 하나가 발생합니다.
sed: -e expression #5, char 5: unterminated `s' command
내가 여기서 무엇을 놓치고 있는 걸까요?
답변1
당신이 놓치고 있는 가장 중요한 것은 $
특수 문자를 만드는 것이고 Make와 Shell의 인용이 다릅니다.
예를 들어
's/$/\"/g'
`` 내부의 모든 것을 셸로 보호하지만( \
그런데 불필요한 작업도 수행함) 그렇지 않으므로 다음과 같이 보이게 만듭니다.
's/\"/g'
이름이 지정된 변수가 없다고 가정합니다 /
(make에서는 가능하지만 일반적으로 셸에서는 불가능함).
가장 먼저 할 일은 $
로 교체하는 것입니다 $$
.
답변2
{}
실행하는 인라인 스크립트 내에서 사용되는 것은 find
코드 주입 취약점입니다. 그러지 마세요. sh -c
(스크립트의) 첫 번째 인수는 작은따옴표로 묶어야 하며 스크립트의 인수는 명령줄에 전달되어야 합니다.
대신 find
다음과 같이 명령을 작성합니다( 한 곳에서 사용할 수 없는 bash
대신 사용).sh
${parameter//pattern/word}
find stdlib/main -type f -exec bash -c '
for pathname do
string=${pathname#stdlib/main/} # delete initial path
string=${string%.*} # delete suffix after last dot
string=${string////.} # change slashes to dots
# output:
printf "resource:\"%s,%s\"\n" "$pathname" "$string"
done' bash {} +
이는 을 사용하는 것이 아니라 sed
매개변수 대체를 사용하여 발견된 경로 이름을 수정하는 것입니다 find
. 인라인 bash
스크립트는 발견된 파일 배치에서 실행되며 각 배치의 경로 이름을 반복합니다. printf
명령이 실행되는 것과 같은 방식으로 변환된 데이터를 출력합니다. ( sed
만약 내가 그것을 올바르게 해독했다면, 그것은 그렇지 않습니다.오직당신이 설명하는 것).
나중에 큰따옴표와 쉼표가 포함된 파일 이름을 어떻게 처리할지는 또 다른 질문입니다(출력 문자열은 나중에 구문 분석하기 어려울 수 있습니다 resource:
).
가장 쉬운 방법은 find
명령을 별도의 스크립트에 넣고 make
GNU에서 호출하는 것입니다 $(shell ...)
. 그렇지 않으면 다음과 같은 결과가 나올 것입니다.
STDLIB_RESOURCES := $(shell \
find stdlib/main -type f -exec bash -c ' \
for p do \
s=$${p\#stdlib/main/}; \
s=$${s%.*}; \
s=$${s////.}; \
printf "resource:\"%s,%s\"\n" "$$p" "$$s"; \
done' bash {} + )
make
또한 ( GNU가 변수를 처리하는 방식 등 으로 인해) Makefile에서 :=
변수에 액세스할 때마다가 아니라 변수에 할당될 때 즉시 이 명령을 실행해야 한다는 점을 참고하세요.
관련된: