0

I have a problem running a line of code in my Makefile that runs well in /bin/bash. I know that Make runs in /bin/sh so I have solved that problem with this guide by declaring to Make that I want it to run in Bash. However, even though I run Make in Bash I still cant get this line to work, even tough it works in Bash just fine.

Why does Bash throw an error when running rm -f !(2).txt in the Makefile but not when executed out in its shell?

EDIT: I seek to delete all files with a certain file extension except one of those files. The command rm -f !(2).txt does just that in my bash terminal but not in Make.

EDIT: When running rm -f !(2).txt "normally" in the terminal with Bash (terminal returned bash when issuing command echo "$0") it works just fine but whey I run /bin/bash -c 'rm -f !(2).txt' I get the same error as in the Makefile.

Makefile

all: clean

clean: SHELL:=/bin/bash
clean:
    rm -f !(2).txt

Running command in Bash

 $ ls
1.txt  2.txt  3.txt  4.txt  Makefile    # Directory has all text files
 $ rm -f !(2).txt                       # Remove all text files but 2.txt
 $ ls
2.txt  Makefile                         # Only 2.txt is not removed

Running command in Bash through Makefile

 $ ls                                   # Directory has all text files
1.txt  2.txt  3.txt  4.txt  Makefile
 $ make clean                           # Running make 
rm -f !(2).txt
/bin/bash: -c: line 0: syntax error near unexpected token `('
/bin/bash: -c: line 0: `rm -f !(2).txt'
make: *** [clean] Error 1
Community
  • 1
  • 1
  • 2
    You need to `shopt -s extglob`, too. – choroba Feb 12 '17 at 15:29
  • If I run your command from the shell, it fails the same way as it does when invoked from make: `/bin/bash -c 'echo !(2).txt'` -> `/bin/bash: -c: line 0: syntax error near unexpected token '('` – MadScientist Feb 12 '17 at 15:34
  • @MadScientist Weird. Running it the way you did ended in the same error. However, running it just in my terminal gives me the expected result. My terminal returns `bash` when I run `echo "$0"` so what can be the hickup? – Petter Karkea Feb 12 '17 at 15:39
  • The issue was explained below. – MadScientist Feb 12 '17 at 17:05

2 Answers2

2

You need to enable the extglob shell option in order to make it work. In GNU make, it can be achieved by

SHELL := /bin/bash
.SHELLFLAGS := -O extglob -c

Edit by question author

The example with the two rows of code above did not work for me but as this fellow found out, there seems to be a different solution depending on versions.

Hmm. For some reason the .SHELLFLAGS didn't work for me, but putting the flags directly in SHELL=/bin/bash -O extglob -c did. [...] – Timothy Jones

The example @choroba uses works in Make 4.0 but did not work for me since I have Make 3.81. However, here is my solution:

all: clean

clean: SHELL=/bin/bash -O extglob -c
clean:
    rm -f !(2).txt
Community
  • 1
  • 1
choroba
  • 231,213
  • 25
  • 204
  • 289
  • Ok but I still get an error. This is my Makefile now... `clean: SHELL:=/bin/bash clean: .SHELLFLAGS := -O extglob -c clean: rm -f !(2).txt` ...and I get the same error. – Petter Karkea Feb 12 '17 at 15:48
  • @PetterKarkea: It works for me. What version of Make and bash do you run? – choroba Feb 12 '17 at 16:04
  • Bash 4.3.11(1)-release, GNU Make 3.81 – Petter Karkea Feb 12 '17 at 16:23
  • I have slightly more recent ones, but I don't see why it should matter (4.2.53(1), 4.0). – choroba Feb 12 '17 at 16:25
  • Ah! Solved the problem. I had to write it like this: `SHELL=/bin/bash -O extglob -c`. I found the answer [here](http://superuser.com/questions/619849/bash-extended-globbing-inside-a-makefile#comment769890_619851). – Petter Karkea Feb 12 '17 at 16:26
  • Seems like the version difference was important, after all :) – choroba Feb 12 '17 at 16:29
  • 1
    The `.SHELLFLAGS` variable was added in GNU make 3.82. You can check the NEWS file to see what features were added in which releases: http://git.savannah.gnu.org/cgit/make.git/tree/NEWS – MadScientist Feb 12 '17 at 17:05
0

Make has no idea what "!(2).txt" means, if you really must use this construct, then use the $(shell) function:

%(shell rm -f !(2).txt)

Personally, I would change your file naming scheme or build the file names you want to delete in a make variable.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Rich
  • 640
  • 5
  • 12
  • 1
    `!(2).txt` doesn't mean anything to a default bash either, so it won't work in `%(shell ...)` without preparation described in choroba's answer. – Charles Duffy Feb 12 '17 at 16:28