Search code examples
shellg++makefileecho

Hash inside Makefile shell call causes unexpected behaviour


The following command prints the absolute path of a particular C++ header, according to where g++ believes it to be.

echo \#include\<ham/hamsterdb.h\> | g++ -M -x c++-header - | grep hamsterdb.hpp | sed -e 's/-:  //' -e 's/ \\//'

On my system, this outputs: /usr/local/include/ham/hamsterdb.hpp

I run into a problem when trying to run this inside a Makefile in order to set a variable:

FILE=$(shell echo \#include\<ham/hamsterdb.h\> | g++ -M -x c++-header - | grep hamsterdb.hpp | sed -e 's/-:  //' -e 's/ \\//')

.PHONY spec

spec:
    @echo $(FILE)

This outputs a new line. I think it's the hash ('#') character which is messing with make; if I rewrite the FILE=... line like this:

FILE=$(shell echo \#include\<ham/hamsterdb.h\>)

the output is still nothing.


Solution

  • You have to escape the hash twice in order to use it inside functions: once for Make and once again for the shell.

    That is,

    FILE = $(shell echo \\\#include\ \<ham/hamsterdb.h\>)
    

    Notice three backslashes instead of two as one could expect. The third backslash is needed because otherwise the first one would escape the second and the hash would be still unescaped.

    UPD.

    Another possible solution is to only escape the hash for Make and use Bash single quotes to prevent interpreting the hash as a shell comment. This also eliminates the need of escaping spaces, < and >:

    FILE = $(shell echo '\#include <ham/hamsterdb.h>')