내 find(+ sed) 명령이 터미널에서는 작동하지만 makefile에서는 작동하지 않는 이유는 무엇입니까?

내 find(+ sed) 명령이 터미널에서는 작동하지만 makefile에서는 작동하지 않는 이유는 무엇입니까?

다음 명령이 있습니다.

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명령을 별도의 스크립트에 넣고 makeGNU에서 호출하는 것입니다 $(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에서 :=변수에 액세스할 때마다가 아니라 변수에 할당될 때 즉시 이 명령을 실행해야 한다는 점을 참고하세요.

관련된:

관련 정보