我有以下命令:
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
我在这里缺少什么?
答案1
您缺少的主要内容是要$
制作一个特殊字符,并且 Make 和 shell 中的引用是不同的。
例如
's/$/\"/g'
`` 对将它们内部的所有内容保护到外壳(并且顺便做了\
不必要的事情),但不做,所以使它看起来像
's/\"/g'
假设你没有一个名为的变量/
(你可以在 make 中,但通常不在 shell 中)。
首先要做的就是替换$
为$$
.
答案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
命令放入单独的脚本中并从 GNU make
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 中(由于 GNUmake
处理变量等的方式)还要注意:=
.您需要这个命令才能在分配给变量时立即执行,而不是每次访问变量时都执行。
有关的: