Q:

Handling File Names With Parentheses in Makefile

I am trying to write a Makefile rule that can detect whether files have been added, deleted, or changed in the git repository.  What I have currently doesn't work if the file names have parentheses.  This shows the problem.
$ 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

It looks like it's interpreting the  parentheses as a shell directive instead of part of the file name.  Is there a way to resolve this?

A:

This isn't so much a git thing, or even all that much of a make thing, it's primarily a shell thing.
Remember that each shell command line in a makefile is just fed to the shell (one line at a time, at that).  It's the shell that thinks the parentheses are to be interpreted.  To prevent the shell from interpreting them, you can use (any kind of) quote marks, e.g.:
echo '$(STATUS)'

Single quotes still work here since it's make that expands $(STATUS) and it ignores the quotes at this level.
The caveat here is that if the variable that make expands itself has any of these quotes in it, the shell will interpret those as well.  So if you have a file named '(gotcha)', your original shell command will work, and the quoted version will fail!
There's a more general principle at work here, which is that inputs need to be sanitized whenever you pass them to something that does interpretation (such as a shell).  If you can be sure that all your file names will be "clean" you can let them pass through to the shell, but as you have seen, if some of your file names might have "interesting-to-the-shell" characters, you would have to protect against that.
In this particular case, it's hard to guess what you eventually want to do with the output.  If you can do all your testing in gmake code (so that the expanded variable never passes on to the shell), that's one way to handle it.  If you set the variable in the shell code itself, and are careful with your shell quoting there, that's another way to handle it.  You can use even $(subst) to protect all the metacharacters individually, although this is quite painful.
As Etan Reisner noted in a comment, though, since single quotes turn off most shell metacharacters, we just need to make sure that any single quotes in the data are transformed appropriately:
echo '$(subst ','\'',$STATUS))'

does the trick in this case.  However, the command run (echo) can no longer tell that there was a series of file names here: it just gets one big argument with embedded spaces, and there is no way to distinguish, e.g., 'file1 file2' (two files) from 'file1 file2' (a single file with a space in its name).

