Bash – Remove all files, except for two

bashwildcards

I would like to delete everything in one folder, with the exception of two files:

exec.js
.git

I found this:

rm !(exec.js|.git)

Which works in the CL, but when I try to store in a sh file, and call with bash file.sh, this happens:

test.sh: line 1: syntax error near unexpected token `('
test.sh: line 1: `rm !(exec.js|.git)'

Any ideas why?

Best Answer

You need

shopt -s extglob

inside the script.

Your bash configuration presumably turns this on automatically for interactive shells (perhaps because you load the bash-completion package for context-sensitive completion); you need to turn it on manually inside scripts.

Also make sure that your script starts with #!/bin/bash (or a variant like #!/usr/bin/env bash, otherwise it may be executed by a different shell that doesn't support this syntax.

For example, this script will create a few files and then remove all except the two you want to keep:

$ cat x
#!/bin/bash

shopt -s extglob

touch f1 f2 f3 exec.js .git foo bar

echo Before: $(ls -A)

rm !(exec.js|.git)

echo After: $(ls -A)

In execution:

$ bash x
Before: .git bar exec.js f1 f2 f3 foo x
rm: cannot remove '.': Is a directory
rm: cannot remove '..': Is a directory
After: .git exec.js

Note that you get errors with . and ..; you'll have seen those on the command line as well:

$ touch f1 f2 f3 exec.js .git foo bar

$ rm !(exec.js|.git)
rm: cannot remove '.': Is a directory
rm: cannot remove '..': Is a directory
Related Question