Escaping # in gmake $(shell) function

makefile escape special characters
makefile escape quotes
makefile escape parentheses
makefile shell function
makefile escape single quote
gnu makefile shell function
makefile escape colon
makefile shell command

I have an environment variable GITHUB_REFS that I want to perform some bashism on and capture the result in another variable GITHUB_BRANCH from a GNU makefile. My naive approach looks like this:

SHELL:=/bin/bash
GITHUB_BRANCH:=$(shell echo "${GITHUB_REF#refs/heads/}")

If I run the bashism by itself, it works fine however when running the makefile above it fails with:

Makefile:2: *** unterminated call to function 'shell': missing ')'.  Stop.

I tried escaping the # as \#, since it is a plausible culprit, and indeed then the Makefile works however the bashism does not. Double escaping it gives the same error again.

So how can I pull this off?

You also need to double the dollar sign to pass it through to the shell.

GITHUB_BRANCH:=$(shell echo "$${GITHUB_REF\#refs/heads/}")

For what it's worth, this simple parameter expansion is portable to any reasonably modern sh, so not at all an exclusive Bash feature.

Of course, make is perfectly capable of performing the same substitution, without invoking an external process.

GITHUB_BRANCH := $(patsubst refs/heads/%,%,${GITHUB_REF})

GNU Make Escaping: A Walk on the Wild Side, You can't escape a newline, there's no syntax for special characters (e.g. you can' t write \n), and even the $(shell) function strips newlines from� GNU Make's escaping rules GNU Make's handling of 'tab' as the start of a command is a legendary language feature, but some other special characters can trip you up. GNU Make's handling of $, %, ?, *, [, ~, \, and # are all special. Every GNU Make user is familiar with $ for starting a macro reference.

The # can be escaped using \, but you also forgot to escape the $.

This Makefile works:

SHELL:=/bin/bash

foo := $(shell echo "$${SHELL\#/bin/}")

all:
    echo $(foo)

Shell Function (GNU make), != ' assignment operator is used, its exit status is placed in the .SHELLSTATUS variable. Here are some examples of the use of the shell function: contents := $(� The shell function performs the same function that backquotes (‘`’) perform in most shells: it does command expansion. This means that it takes as an argument a shell command and evaluates to the output of the command. The only processing make does on the result is to convert each newline (or carriage-return / newline pair) to a single

Just to note that this (the need to escape the #) is a bug, and will be fixed in an upcoming version of GNU make. If you want to allow your makefile to be portable before/after the bug is fixed, you should hide it in a variable like this:

HASH := \#

foo := $(shell echo "${GITHUB_REF$(HASH)refs/heads/}")

This will work in all versions of GNU make.

Escaping # in gmake $(shell) function, Escaping # in gmake $(shell) function some bashism on and capture the result in another variable GITHUB_BRANCH from a GNU makefile. Nota bene: if you are using gmake 3.82 or later, you can enable the .ONESHELL feature, which causes gmake to invoke the entire recipe using a single shell invocation, even if you haven’t used line continuations. The $(shell) function. The $(shell) function is the second way to invoke the shell from gmake. It’s intended purpose is to capture

Escape $ dollar sign on Makefiles, Escape $ dollar sign on Makefiles. I just learned that if on shell: #!/bin/bash for i in *; do echo "i=$i"; done. Bash. on Makefile: #!make my-files:� makefile,make,gnu-make. Instead of using relative path, you can use absolute path by using the realpath or abspath functions: $(realpath names…) For each file name in names return the canonical absolute name. A canonical name does not contain any . or .. components, nor any repeated path separators (/) or symlinks. In

GNU make: Functions, GNU make: Functions. $(patsubst pattern , replacement , text ) Replacing the variable reference ' $(needs_made) ' with the function call It resembles the for command in the shell sh and the foreach command in the This means you may need to provide extra levels of escaping for “$” characters when using eval . GNU make. This file documents the GNU make utility, which determines automatically which pieces of a large program need to be recompiled, and issues the commands to recompile them. This is Edition 0.75, last updated 17 January 2020, of The GNU Make Manual, for GNU make version 4.3.

GNU make: Makefiles, You cannot use comments within variable references or function calls: any instance you do this by escaping the internal newlines with a backslash ( \ ) character. and c.mk , and $(bar) expands to bish bash , then the following expression. How can I get gmake to output return codes for all commands without modifying makefile. makefile,make,return-value,gnu-make. Note that all your suggested workarounds are broken: because you are now running echo as the last command in the recipe, the recipe will always exit with the return code of echo, which is always success.

Comments
  • Have you tried defining a Make variable containing only #? That's a technique that's often used for spaces and the like. Additionally, you forgot to escape the $ - that should be $${GITHUB_REF#refs/heads/}
  • Shouldn't you close the bracket before, like this ? $(shell echo "${GITHUB_REF}#refs/heads/")
  • @Tim No, the OP is attempting to use the shell's parameter expansion feature.
  • Then I'd suggest escaping both $ and # by using $$ and \#
  • FWIW, The original Bourne shell did not support that expansion syntax. See en.wikibooks.org/wiki/Bourne_Shell_Scripting/Variable_Expansion. It was first introduced, IIRC, in ksh88. However, you'd be hard pressed to find any working UNIX system where /bin/sh is really the Bourne shell rather than dash, bash, etc.