I want to create a file during a rule of a makefile,
At the end of the rule, I want to run a shell command for the file by using $(shell xxx) function of make.
This is simplified example makefile for showing the problem:
test:
#echo 123 >> file
#cat file
#echo "File: $(shell cat file) "
I expected that the last line of the makefile would print: File: 123
Instead it looks like that make runs $(shell cat file) before other lines of the makefile:
> rm file
> make test
cat: file: No such file or directory
123
File:
Is there some simple way to prevent the unwanted behavior, and get the following kind of result?
> rm file
> make test
123
File: 123
There's no need to use make's $(shell ... syntax here. Just use a backtick `...` sequence to invoke cat directly...
test:
#echo 123 >> file
#cat file
#echo "File: "`cat file`
Or, if your version of echo supports the option to suppress the trailing newline...
test:
#echo 123 >> file
#cat file
#echo -n "File: "; cat file
Is there some simple way to prevent the unwanted behavior
This is expected and documented behaviour. If that's not what you want, you mustn't use $(shell) at all.
test:
#echo 123 >> file
#cat file
#printf "File: "
#cat file
Escape your dollars in Makefile and use shell's own subprocess facility, $(command).
test:
echo "zzz" > z
echo "$$(cat z)"
Related
This question already has an answer here:
How To Loop through n Test cases In Makefile?
(1 answer)
Closed last year.
I am using makefile to compile my program. After compiling, I want to do some test automatically and it will take several different files as the input. I am trying to do something like this:
test:
gcc test.c -o test
run:
./test test1.nlb >> test_output.txt
./test test2.nlb >> test_output.txt
...
We may have more test files so I am thinking whether there is a way I can avoid typing all these names. I have tried something like:
FILE = $(wildcard *.nlb)
run:
$(foreach file,$(FILE),./test $(file) >> test_output.txt)
But it didn't work.
Any ideas on how to write such a makefile? Thanks in advance!
Suppose the wildcard expanded to foo.nlb bar.nlb baz.nlb. Then your recipe:
$(foreach file,$(FILE),./test $(file) >> test_output.txt)
would expand to:
./test foo.nlb >> test_output.txt ./test bar.nlb >> test_output.txt ./test baz.nlb >> test_output.txt
which as you can clearly see, is not a valid shell command. Remember, make functions (with some special exceptions) are basically text manipulation functions. They don't run commands, they just massage text.
You have two choices: you can either write your loop using shell syntax, not make syntax:
for file in $(FILE); do ./test $$file >> test_output.txt; done
Or you can add a semicolon into your make foreach loop, to separate the commands:
$(foreach file,$(FILE),./test $(file) >> test_output.txt ;)
^^
which will expand to:
./test foo.nlb >> test_output.txt ; ./test bar.nlb >> test_output.txt ; ./test baz.nlb >> test_output.txt
which is a valid shell script.
Or better, use && as a separator so if a test fails you'll notice it:
$(foreach file,$(FILE),./test $(file) >> test_output.txt &&)
I encounter a strange behavior of addprefix with -e. The expected -e for the first word is not added. Here's the makefile to reproduce it.
a := file1 file2 file3
b := $(addprefix -s , $(a))
c := $(addprefix -e , $(a))
all:
#echo $(b)
#echo $(c)
Result:
-s file1 -s file2 -s file3
file1 -e file2 -e file3
Is it a bug or something is missing?
You should never use echo to print any string that is not just simple text that you know contains no starting dash characters. The echo command is not really standardized and there are a lot of versions, some of which accept options and some of which don't.
On your system, the echo command accepts a -e option so when you use echo -e file -e file2 -e file3 echo eats the first -e.
You can use:
all:
#printf '%s\n' '$(b)'
#printf '%s\n' '$(c)'
to be portable. Or you can use make's built-in functions:
all:
$(info $(b))
$(info $(c))
Makefile
a:
echo "123" > afile
cat afile
# expect output 123, but it's empty
echo $(shell cat afile)
Why does echo output nothing?
In make, the entire recipe (all commands in the recipe) are expanded before the first command in the recipe is invoked.
Since $(shell ...) is a make function, not a shell function, make will expand it before it starts running any commands in the recipe.
You can change it to $$(cat afile) and make it a shell operation, and make won't run the cat.
thank you all. $(shell ...) expanded before recipe executing. make sense.
look following code
a:
ln -s hello.txt qq
cat qq
echo $(shell cat qq)
a1: a
cat qq
echo $(shell cat qq)
make a1.
why a1 could echo string right? hello.txt contains hello.
after google, find the answer. like MadScientist said avoid $(shell ...).
a:
echo "123" > afile
cat afile
echo `cat afile`
a:
echo "123" > afile
cat afile
echo $$(cat afile)
In my Makefile deploy target I create environment variables and I want to reuse those in the following lines:
SHELL=/bin/sh
deploy:
export $(shell sh this-script-generate-key-values.sh | xargs)
echo ${VAR1} #there is no variable here
echo ${VAR2} #there is no variable here
Where:
this-script-generate-key-values.sh generates this output:
VAR1="somevalue"
VAR2="somevalue"
Why the variables are not set in subsequent lines? How can I make it work?
Notes:
This line works: sh this-script-generate-key-values.sh | xargs
The shell must be /bin/sh (no bash)
All lines in a Makefile recipe run in a separate shell. You need to run the lines in a single shell. Also you need to escape the dollar sign ($) so that variable substitution is not done by make but by the shell.
SHELL=/bin/sh
deploy:
export $$(this-script-generate-key-values.sh | xargs) ;\
echo $${VAR1} ;\
echo $${VAR2}
Just to expand on my comment -- you could output to a file, and use the file to generate your output as so:
vars.txt:
this-script-generate-key-values.sh > $#
deploy : vars.txt
echo VAR1=$$(sed -n 's|VAR1=\(.*\)|\1|p' vars.txt)
echo VAR2=$$(sed -n 's|VAR2=\(.*\)|\1|p' vars.txt)
note: you may have to generate dependencies for vars.txt or declare it .PHONY, otherwise, this will not run on every invocation of make.
If the .ONESHELL special target appears anywhere in the makefile then all recipe lines for each target will be provided to a single invocation of the shell. Newlines between recipe lines will be preserved.
.ONESHELL:
deploy:
export $$(this-script-generate-key-values.sh)
echo $${VAR1}
echo $${VAR2}
This question already has answers here:
How to run sub Shell script in Makefile?
(2 answers)
Closed 2 years ago.
I am having an issue with my Makefile:
At the begining of the Makefile, a file is created.
I wanna retrieve the content of that created file by using the "cat command".
I need to use the $(shell cat) execution because I have to retrive it inside another command.
It's like if the sub-shell does not reallize of the creation of that file and does not find it.
If you take a look into the output message order... its like first of all, it is executing the $(shell cat) command, because the first line is saying: "cat: test.txt: The file or directory does not exist".
Why it seems to be executing the commands unordered... executing first the $(shell cat)...
It could be shown in the following silly example:
Take a look into the following Makefile with a test routine:
test:
rm -rf test.txt
echo "Hello World" > test.txt
echo "$(shell cat test.txt)"
Executing make test the Output is as follows:
cat: test.txt: The file or directory does not exist.
rm -rf test.txt
echo "Hello World" > test.txt
echo ""
If you execute "make test" twice, you can realize that the second execution of the Makefile is echoing the text "Hello World"... because it is executing $(shell cat test.txt) at the begining and the file exists from the frist "make test" execution...
Any suggestion about what is happening and how I have to proceed to accomplish my goal?
Many thanks in advance!
Double up your $s to escape them, so they're still seen by the shell in literal form (and thus executed by the shell, not by make prior to the shell's invocation):
test:
rm -rf test.txt
echo "Hello World" > test.txt
echo "$$(cat test.txt)"