I have a directory where I have some C program files and a Makefile with executables of those C files. I can simply run this to remove all the executable at once in rm !(*.c|Makefile)
.
But when i am adding this to my makefile
CFLAGS= -Wall -g -lm
SHELL=/bin/bash
careful:
echo "nothing"
clean:
rm -i !(*.c|Makefile)
I am getting this error while executing the command
/bin/bash: -c: line 1: syntax error near unexpected token `('
/bin/bash: -c: line 1: `rm -i !(*.c|Makefile)'
make: *** [Makefile:8: clean] Error 2
I am not too knowledgeable about Bash syntax but I know that ()
is treated as a subshell, I think that's why I can't run rm (*.c)
directly in the terminal because *.c
is not a command (or any valid syntax). But running rm -i !(*.c)
works in the terminal. So I guess !()
is treated differently in Bash.
My assumption on all this: in Makefile when I am running make clean
it is treating !(*.c|Makefile), differently than in normal terminal, or somehow it is disregarding !
in !(*.c|Makefile)
So my questions are:
- Are
!()
and()
treated differently in Bash? - Why does
!(wildcard)
work in the terminal but not in myMakefile
- How can I make it work in the
Makefile
?
CodePudding user response:
The Bash-specific extended globbing patterns are not enabled out of the box for noninteractive shells.
!(...)
is purely part of this wildcard syntax, and has nothing with subshells to do.
Probably a better solution anyway is to refactor this so you don't depend on Bash.
.PHONY: clean
clean:
rm -i $(filter-out $(wildcard *.c) Makefile,$(wildcard *))
The Bash Reference Manual doesn't have a good anchor to link to, but the extended globbing syntax available with extglob
is described in the section on Pattern Matching
If you really wanted to, you could shopt -s extglob
in your Makefile
, too, but this gets rather complicated as you will have to hide the statement from the parser until the shell is configured to understand this syntax. For example,
.PHONY: clean
clean:
shopt -s extglob; \
eval 'rm -i !(*.c|Makefile)'
Notice also how shopt
and eval
need to be on the same logical line, as make
out of the box executes each recipe line in a separate new shell instance (though you can change that with .ONESHELL
)