问题
Does anyone know how to use a here-document redirection on a recipe?
test:
sh <<EOF
echo I Need This
echo To Work
ls
EOF
I can't find any solution trying the usual backslash method (which basically ends with a command in a single line).
Rationale:
I have a set of multi-line recipes that I want to proxy through another command (e.g., sh, docker).
onelinerecipe := echo l1
define twolinerecipe :=
echo l1
echo l2
endef
define threelinerecipe :=
echo l1
echo l2
echo l3
endef
# sh as proxy command and proof of concept
proxy := sh
test1:
$(proxy) <<EOF
$(onelinerecipe)
EOF
test2:
$(proxy) <<EOF
$(twolinerecipe)
EOF
test3:
$(proxy) <<EOF
$(threelinerecipe)
EOF
The solution I would love to avoid: transform multiline macros into single lines.
define threelinerecipe :=
echo l1;
echo l2;
echo l3
endef
test3:
$(proxy) <<< "$(strip $(threelinerecipe))"
This works (I use gmake 4.0 and bash as make's shell) but it requires changing my recipes and I have a lot. Strip removes the newlines, from the macro, then everything is written in a single line.
My end goal is: proxy := docker run ...
回答1:
Using the line .ONESHELL: somewhere in your Makefile will send all recipe lines to a single shell invocation, you should find your original Makefile works as expected.
回答2:
When make sees a multi-line block in a recipe
(i.e., a block of lines all ending in \
, apart from the last),
it passes that block un-modifed to the shell.
This generally works in bash,
apart from here docs.
One way around this is to strip any trailing \
s,
then pass the resulting string to bash's eval
.
You do this in make by playing with ${.SHELLFLAGS}
and ${SHELL}
.
You can use both of these in target-specific form if you only want it to kick in for a few targets.
.PHONY: heredoc
heredoc: .SHELLFLAGS = -c eval
heredoc: SHELL = bash -c 'eval "$${@//\\\\/}"'
heredoc:
@echo First
@cat <<-there \
here line1 \
here anotherline \
there
@echo Last
giving
$ make
First
here line1
here anotherline
Last
Careful with that quoting, Eugene. Note the cheat here: I am removing all backslashes, not just the ones at the ends of the line. YMMV.
回答3:
With GNU make, you can combine multi-line variables with the export directive to use a multi-line command without having to turn on .ONESHELL
globally:
export define script
cat <<'EOF'
here document in multi-line shell snippet
called from the "$@" target
EOF
endef
run:; @ eval "$$script"
will give
here document in multi-line shell snippet
called from the "run" target
You can also combine it with the value function to prevent its value from being expanded by make:
define _script
cat <<EOF
SHELL var expanded by the shell to $SHELL, pid is $$
EOF
endef
export script = $(value _script)
run:; @ eval "$$script"
will give
SHELL var expanded by the shell to /bin/sh, pid is 12712
回答4:
Not a here doc but this might be a useful workaround. And it doesn’t require any GNU Make’isms. Put the lines in a subshell with parens, prepend each line with echo. You’ll need trailing sloshes and semi-colon and slosh where appropriate.
test:
( \
echo echo I Need This ;\
echo echo To Work ;\
echo ls \
) \
| sh
来源:https://stackoverflow.com/questions/35516379/makefile-recipe-with-a-here-document-redirection