shell - How to assign the output of a command to a Makefile variable

ID : 10179

viewed : 38

Tags : shellmakefileshell

Top 5 Answer for shell - How to assign the output of a command to a Makefile variable

vote vote

95

Use the Make shell builtin like in MY_VAR=$(shell echo whatever)

me@Zack:~$make MY_VAR IS whatever 

me@Zack:~$ cat Makefile  MY_VAR := $(shell echo whatever)  all:     @echo MY_VAR IS $(MY_VAR) 
vote vote

82

Beware of recipes like this

target:     MY_ID=$(GENERATE_ID);     echo $MY_ID; 

It does two things wrong. The first line in the recipe is executed in a separate shell instance from the second line. The variable is lost in the meantime. Second thing wrong is that the $ is not escaped.

target:     MY_ID=$(GENERATE_ID); \     echo $$MY_ID; 

Both problems have been fixed and the variable is useable. The backslash combines both lines to run in one single shell, hence the setting of the variable and the reading of the variable afterwords, works.

I realize the original post said how to get the results of a shell command into a MAKE variable, and this answer shows how to get it into a shell variable. But other readers may benefit.

One final improvement, if the consumer expects an "environment variable" to be set, then you have to export it.

my_shell_script     echo $MY_ID 

would need this in the makefile

target:     export MY_ID=$(GENERATE_ID); \     ./my_shell_script; 

Hope that helps someone. In general, one should avoid doing any real work outside of recipes, because if someone use the makefile with '--dry-run' option, to only SEE what it will do, it won't have any undesirable side effects. Every $(shell) call is evaluated at compile time and some real work could accidentally be done. Better to leave the real work, like generating ids, to the inside of the recipes when possible.

vote vote

80

Wrapping the assignment in an eval is working for me.

# dependency on .PHONY prevents Make from  # thinking there's `nothing to be done` set_opts: .PHONY   $(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output) 
vote vote

66

Here's a bit more complicated example with piping and variable assignment inside recipe:

getpodname:     # Getting pod name     @eval $$(minikube docker-env) ;\     $(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))     echo $(PODNAME) 
vote vote

50

With GNU Make, you can use shell and eval to store, run, and assign output from arbitrary command line invocations. The difference between the example below and those which use := is the := assignment happens once (when it is encountered) and for all. Recursively expanded variables set with = are a bit more "lazy"; references to other variables remain until the variable itself is referenced, and the subsequent recursive expansion takes place each time the variable is referenced, which is desirable for making "consistent, callable, snippets". See the manual on setting variables for more info.

# Generate a random number. # This is not run initially. GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)  # Generate a random number, and assign it to MY_ID # This is not run initially. SET_ID = $(eval MY_ID=$(GENERATE_ID))  # You can use .PHONY to tell make that we aren't building a target output file .PHONY: mytarget mytarget: # This is empty when we begin     @echo $(MY_ID) # This recursively expands SET_ID, which calls the shell command and sets MY_ID     $(SET_ID) # This will now be a random number     @echo $(MY_ID) # Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)     $(SET_ID) # This will now be a different random number     @echo $(MY_ID) 

Top 3 video Explaining shell - How to assign the output of a command to a Makefile variable

Related QUESTION?