如何阻止GNU干扰包含美元符号的文件名?

时间:2017-11-21 17:32:12

标签: makefile gnu

所以我正在编译java代码,它通常会创建文件名中有美元符号的.class文件。 GNU make似乎讨厌这个并且不断干扰文件名。

作为一个简单的(人为的)示例,假设我想创建一个GNU Makefile任务,它将对MyFolder目录中的所有.class文件(以及它的子文件夹)执行hexdump。

现在假设MyFolder有以下文件:

MyFolder/makefile
MyFolder/sample.java
MyFolder/subdir/abc$1.class
MyFolder/subdir/test.java
MyFolder/xyz.class

目标是编写一个运行hexdump的配方,将其传递给MyFolder(及其子文件夹)中的所有.class文件。因此,对于上面的文件列表,GNU make应该执行的最终命令是:

hexdump subdir/abc$1.class xyz.class

这应该像从日志中掉落一样容易,但GNU make似乎已经付出了极大的痛苦,使其尽可能地难以实现。使用以下Makefile的天真尝试不起作用:

default:
    hexdump $(shell find -name '*.class')

如果运行此Makefile,则会出现以下错误:

hexdump ./subdir/abc$1.class ./xyz.class
hexdump: ./subdir/abc.class: No such file or directory
make: *** [makefile:3: default] Error 1

请注意,在上面输出的第一行中,命令是否正确(文件名包含美元符号字符)。但是后来执行的实际命令“$ 1”已经被评估了。

事实上,即使是下面的Makefile也会失败,当用双美元符号正确地转出美元符号时。当然我想编写一个不会对文件名进行硬编码的配方,但是应该使用$(shell find ...)命令找到文件

default:
    hexdump subdir/abc$$1.class xyz.class

即使上面的Makefile失败也会出现与第一个Makefile相同的错误。

如何阻止GNU make干扰包含美元符号的文件名? java编译器一直在创建这些东西,所以肯定有人之前必须遇到这个问题吗?

2 个答案:

答案 0 :(得分:4)

在配方中以这种方式使用时,Make不会再次解释$(shell …)输出。问题是 shell 解释$1并将其扩展为第一个(不存在的)参数,即空字符串。

如何解决这个问题取决于遇到此问题时您正在做什么。 GNU make不提供任意的shell引用函数,并且无论如何都会出现包含空格的文件名的问题。您可以尝试通过使用'包围文件名来避免Java类文件的参数替换:

hexdump $(patsubst %,'%',$(shell find -name '*.class'))

另一种方法是在shell中创建文件列表,使用类似:

find -name -print0 | xargs -0 hexdump

这完全避免了shell对字符串的解释。即使有了

hexdump $$(find -name '*.class')

shell不会解释文件名中的$符号,因为命令替换输出仅受分词和路径名扩展(globbing)的影响,但不是参数替换。

答案 1 :(得分:1)

首先,为输出文件选择这样一个愚蠢的命名约定确实是Java的错误。

其次,尝试用make构建Java代码几乎肯定是一种令人沮丧的练习。 Make的构建是为了使用主文件作为输入并生成一个文件作为输出的工具,其中输出文件名与输入文件名相关。 Java不是那样的。

但是,要直接回答您的问题,问题是$是一个特殊字符,因为它引入了make变量。您必须使用$$转义它,如上例所示。

但是,这还不够,因为$ 对shell来说是特殊的,所以如果你在食谱中使用$,你也必须从shell使用单引号或反斜杠。所以,你的最终命令将是:

default:
        hexdump 'subdir/abc$$1.class' xyz.class

或:

default:
        hexdump subdir/abc\$$1.class xyz.class