2016-01-28 33 views
1

我试图编写一个Makefile规则,该规则可以检测git存储库中的文件是否已被添加,删除或更改。如果文件名有括号,我目前无法工作。这显示了问题。在Makefile中处理带圆括号的文件名

$ 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

使用单引号而不是双引号可能会有所帮助。 –

+0

不是。我只是试了一下。问题在于'Makefile'中的git,Makefile中没有任何引号。 这是我想用一个带有括号的文件名的现有回购做的事情。我刚把它简化为一个简单的测试用例。 –

+0

只是出于好奇。为什么你会在他们的名字中使用括号的源文件? –

回答

1

这不是一个的东西,或甚至所有的一个东西,它主要是一个的事情。

请记住,makefile中的每个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'(一个名字中有一个空格的文件)。

+0

引用一个字符串以嵌入单引号字符串非常简单。你只需用''''替换所有的单引号,然后用单引号包装它。 –

+0

@EtanReisner:好点;我在考虑一般情况,而不是使用shell的细节。我会编辑。 – torek

相关问题