I am writing a script to create a Makefile. I used a for-loop to iterate through all my arguments to 'echo ... >> Makefile' into the command section of each target. The expected output goes something like this:

$ makemake.sh a.out -Hello -World
$ cat Makefile

> a.out : appointment.o calendar.o day.o dayofweek.o time.o year.o 
>   g++ -ansi -Wall -g -o a.out -Hello -World

However, using the technique above:

echo -n "g++ -ansi -Wall -g -o " >> Makefile
  for arg in $@; do
    echo -n "$@ " >> Makefile

Yields the following:

a.out : appointment.o calendar.o day.o dayofweek.o time.o year.o 
    g++ -ansi -Wall -g -o a.out -Hello -World a.out -Hello -World a.out -Hello -World 

My professor recommended I use shift, but this would make it more difficult to recall arguments for other targets.

Why is this happening and what can I do? Though I still seek an answer, I am very interested in the logic behind this reaction.

  • Did you want to try using $arg in that loop? Commented Apr 20, 2017 at 1:58
  • 1
    each line in a makefile runs in a separate shell, you need to escape the newlines to make a single line (and then add semicolons to make separate statements)
    – grochmal
    Commented Apr 20, 2017 at 2:03

2 Answers 2


Your code has multiple issues.

  • You are looping over arg but inside the loop you refer to "$@", not to "$arg". (This is, obviously, what causes the problematic output you observe.)
  • Your output needs to contain a tab at the beginning in order for the action line to be valid Makefile syntax.
  • You are not correctly quoting "$@" in the for line.

But apart from that, your code is also needlessly complex. Try this.

printf '\tg++ -ansi -Wall -g -o' >>Makefile
printf ' %s' "$@" >>Makefile

Incidentally, if this is happening inside a control structure of some sort, it's probably both more efficient and more elegant to redirect just once. Each redirection causes the file to be opened and closed separately, and often, the flow is easier if you can just write once, regardless of whether the output file already exists.

if things are as you wish them to be; then
    execute code which generates output
fi >Makefile

In make, you can refer to the target as $@. So you could do this in your case:

shift; echo -e "\tg++ -ansi -Wall -g -o \$@ $@" >> Makefile

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .