私の 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次のようにコマンドを作成します(1つの場所で使用できない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が:=変数にアクセスするたびにではなく、変数に割り当てられたときにすぐにこのコマンドを実行する必要があることに注意してください。

関連:

関連情報