3

This question already has an answer here:

If I want to delete everything in the current directory except for "myfile", I can use

rm -r !("myfile")

But if I put this in a script (called cleanup):

#!/bin/bash
rm -r !("myfile")

I get:

pi@raspberrypi:~/tmp $ ./cleanup
./cleanup: line 2: syntax error near unexpected token `('
./cleanup: line 2: `rm -r !("file2")'

If I run

ps -p $$

I can see that my terminal is using bash,

 PID TTY          TIME CMD
1345 pts/3    00:00:02 bash

so I'm unclear on what the problem is.


Notes:

  1. I realize that if the script actually worked, it would delete itself. So, my script really would be something more like: rm -r !("cleanup"|"myfile"), but the error message is the same either way.
  2. As shown by the blockquote, this is on a Raspbian OS (9 - stretch), which is Debian based.
  3. I feel like this question is bound to be a duplicate, but I can't find it. There is a similarly named question, but it is in regards to inheriting variables, and so doesn't address my issue.

marked as duplicate by Jeff Schaller, Gilles bash Sep 7 '18 at 22:41

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • @roaima Thanks for the edit. How did you get the spaces to work in the last code block? I worked on that for a while and couldn’t figure it out. – Blair Fonville Sep 8 '18 at 0:30
  • To format a code block indent each line with four spaces. Most easily, select it (with the mouse) and click on the {} button in the formatting menu. Keep use of backticks for inline code and you'll be fine. – roaima Sep 8 '18 at 9:43
  • @roaima I must have done that a dozen times and it wouldn’t format properly. I even tried wrapping it in a pre tag. Not sure what was going on. I must have temporarily lost my mind... Thanks again. – Blair Fonville Sep 8 '18 at 14:18
6

The !(pattern-list) pattern is an extended glob. Many distros have it enabled for interactive shells, but not for non-interactive ones. You can check that with

$ shopt extglob
extglob         on
$ bash -c 'shopt extglob'
extglob         off

To fix your script, you have to turn it on: add

shopt -s extglob

at the beginning of it.

4

You are using here an extented globbing feature.

From the bash man page:

If  the  extglob shell option is enabled using the shopt builtin, several extended pattern
matching operators are recognized.  In the following description, a pattern-list is a list
of  one  or more patterns separated by a |.  Composite patterns may be formed using one or
more of the following sub-patterns:   

[...]

       !(pattern-list)
              Matches anything except one of the given patterns

This shell option which can be controlled through the shopt command is per default enabled in interactive shells but disabled per default in non-interactive shells (scripts).

To enable it use shopt -s extglob.

  • Perfect, this worked. – Blair Fonville Sep 7 '18 at 18:14
  • According to the timestamps, it looks like Benjamin beat you by 8 seconds, but I truly appreciate the answer. – Blair Fonville Sep 7 '18 at 18:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.