Back to concepts
Free conceptRecipes and shells

The Make-to-shell boundary

Trace dollar signs, variables, and environment values across Make's recipe expansion and the shell that executes it.

Account for both expanders

Make expands variable and function references in a recipe before handing the resulting text to a shell. A single $ begins a Make reference; $$ becomes one literal dollar sign for the shell.

That is why $${PATH:+:$$PATH} reaches the shell as ${PATH:+:$PATH} instead of asking Make to interpret the shell expression.

Shape the recipe's environment

The recipe shell inherits exported environment values. Assignments and exports made inside that recipe affect the recipe process and its children, not the shell that originally invoked Make.

Use `exec` at the process boundary

A final exec replaces the recipe shell with the selected interactive shell. No dormant wrapper remains to run another command or hide the entered shell's exit status.

Delay PATH expansion until the recipe shell
1enter:2 @PATH="$(root_dir)/.local/bin$${PATH:+:$$PATH}"; \3 export PATH; \4 exec "$(ENTER_SHELL)" -i

GNU Make's $(SHELL) and an exported environment variable named SHELL are different inputs. On Unix, Make does not initialize its own SHELL variable from the environment.

See it in context

Follow the lessons that apply this concept, then inspect the repository at the exact commit behind each explanation.

GNU Make manual: Using Variables in Recipes