3

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.
3
  • @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. Sep 8, 2018 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, 2018 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. Sep 8, 2018 at 14:18

2 Answers 2

7

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.

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

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