在Makefile中使用括号处理文件名

时间:2016-01-28 00:06:15

标签: git makefile

我正在尝试编写一个Makefile规则,该规则可以检测git存储库中是否添加,删除或更改了文件。如果文件名有括号,我目前无法使用。这显示了问题。

$ git init .
Initialized empty Git repository in /home/stephen/w/stack1/.git/
$ cat Makefile
status:
        $(eval STATUS = $(shell git diff-files))
        echo STATUS
        echo $(STATUS)
        echo END STATUS
$ git diff-files
$ echo foo > "(a)"
$ git add "(a)"
$ echo bar >>"(a)"
$ git diff-files
:100644 100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0000000000000000000000000000000000000000 M  (a)
$ make status
echo STATUS
STATUS
echo :100644 100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0000000000000000000000000000000000000000 M (a)
/bin/sh: 1: Syntax error: "(" unexpected
make: *** [status] Error 2

看起来它将括号解释为shell指令而不是文件名的一部分。有办法解决这个问题吗?

1 个答案:

答案 0 :(得分:1)

这不是的事情,甚至不是的事情,它主要是的事情。

请记住,makefile中的每个shell命令行都只是被送到shell(一次一行)。它是认为括号被解释的shell。为了防止shell解释它们,您可以使用(任何类型的)引号,例如:

echo '$(STATUS)'

单引号仍可在此处使用,因为它make扩展$(STATUS)并忽略此级别的引号。

这里需要注意的是,如果make扩展自身的变量中包含任何这些引号,那么shell也会解释它们。因此,如果您有一个名为'(gotcha)'的文件,您的原始 shell命令将起作用,引用的版本将失败!

这里有一个更通用的原则,那就是输入需要消毒每当你将它们传递给做解释的东西时(例如shell)。如果您可以确定所有文件名都是“干净”的,那么您可以将它们传递给shell,但正如您所看到的,如果您的某些文件名可能包含“有趣的shell”字符,你必须要保护它。

在这种特殊情况下,很难猜出你最终想要对输出做些什么。如果你可以在gmake代码中进行所有测试(以便扩展变量永远不会传递给shell),那就是处理它的一种方法。如果你在shell代码中设置变量,并且小心你的shell引用那么,这是另一种处理它的方法。您甚至可以使用$(subst)单独保护所有元字符,尽管这非常痛苦。

作为Etan Reisner noted in a comment,由于单引号关闭了大多数shell元字符,我们只需要确保数据中的任何单引号都已正确转换:

echo '$(subst ','\'',$STATUS))'
在这种情况下,

可以解决问题。但是,命令运行(echo)不能再告诉这里有一系列文件名:它只是一个带有嵌入空格的大参数,并且没有办法区分,例如'file1 file2'来自'file1 file2'的(两个文件)(名称中有空格的单个文件)。