我正在尝试编写一个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指令而不是文件名的一部分。有办法解决这个问题吗?
答案 0 :(得分:1)
这不是git的事情,甚至不是make的事情,它主要是shell的事情。
请记住,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'的(两个文件)(名称中有空格的单个文件)。