Home > database >  Using rm -i !(*.c|Makefile) in Makefile?
Using rm -i !(*.c|Makefile) in Makefile?

Time:03-21

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:

  1. Are !() and () treated differently in Bash?
  2. Why does !(wildcard) work in the terminal but not in my Makefile
  3. 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)

  • Related