為什麼我的 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}
當我自己執行該命令時,它可以完美執行。但我試圖在makefile中使用它:
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
我在這裡想念什麼?
您缺少的主要內容是
$
要製作的特殊字元,並且在 Make 和 shell 中引用是不同的。所以例如
's/$/\"/g'
`` 對將它們內部的所有內容保護到外殼中(順便使
\
不必要的),但不做,所以讓它看起來像's/\"/g'
假設你沒有一個名為的變數
/
(你可以在 make 中,但通常不在 shell 中)。首先要做的是替換
$
為$$
.
{}
在您執行的內聯腳本中使用是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
命令放入單獨的腳本並從 GNUmake
in呼叫它是最簡單的$(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 {} + )
在您的 Makefile 中(由於 GNU
make
處理變數等的方式)還要注意:=
. 您需要這個命令在分配給變數時立即執行,而不是每次訪問變數時。有關的: