Skip to content
shellhacks.dev
Command Line

Run a Makefile Recipe in One Shell with .ONESHELL

Run all lines of a make recipe in a single shell with .ONESHELL, so cd and variables carry over. Only the last line's exit status counts, so add -e to the shell flags.

1 min read
Summarize in AI

A recipe changes directory on one line and uses the new directory on the next, and the second line runs in the wrong place.

Shell
cat > plain.mk <<'EOF'
show:
	@cd /tmp
	@echo "now in $$(pwd)"
EOF
cat > oneshell.mk <<'EOF'
.ONESHELL:
show:
	@cd /tmp
	@echo "now in $$(pwd)"
	@false
	@echo "this line still runs"
EOF
make -f plain.mk
make -f oneshell.mk
echo "exit status: $?"
Text
now in /tmp/run/S1724-1/w
now in /tmp
this line still runs
exit status: 0

By default make starts a new shell for every recipe line, so cd /tmp is forgotten before pwd; the first file printed the original directory. With .ONESHELL: in the makefile, all lines of a recipe are passed to one shell and the second file printed /tmp. The second effect is less visible: the false on the third line did not stop anything, the next line still ran, and make finished with exit status 0. See Using One Shell.

With .ONESHELL only the status of the last command decides success, so add .SHELLFLAGS := -ec to stop at the first failure. The special target applies to the whole makefile wherever it appears, which is why the demonstration uses two files, and it needs GNU Make 3.82 or later. This was run with GNU Make 4.3.

Share

More snippets

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.