Permittivity Permittivity - 5 months ago 16
Linux Question

removing files except some in a bash script

I was trying to delete some files in a bash script and exclude some I still need afterwards. This is what I came up with:

if [ $var1 -eq 0 ]; then
echo "$var1 = 0"
shopt -s extglob
rm /home/someone/!(file1.txt|file2.png|file3.pdf|file4.csv)
else
echo "$var1 = 1"
shopt -s extglob
rm -rf /home/someone/!(file1.txt|file2.png|file3.pdf|file4.csv)
fi


It is working if I execute it manually on the shell, but in the script it does not even call the if-loop. As soon as I comment out the lines containing the "!" it works.

Answer

You cannot turn the extglob shell option from within a block or a function. E.g., this will fail:

shopt -u extglob

f() {
    shopt -s extglob
    echo *+(a)*
}
f

The reason is that the command shopt -s extglob is not executed when the block is parsed… and the parser will hence complain when it encounters the extended glob +(a). See the relevant section of the glob page on Greg's wiki:

extglob changes the way certain characters are parsed. It is necessary to have a newline (not just a semicolon) between shopt -s extglob and any subsequent commands to use it. You cannot enable extended globs inside a group command that uses them, because the entire block is parsed before the shopt is evaluated. Note that the typical function body is a group command. An unpleasant workaround could be to use a subshell command list as the function body.

If you really want this behavior, though, you can use eval, but that's really not recommended at all! Instead, move the shopt -s extglob out of the block. In fact, it is customary to put the shell options at the beginning of the script and use them throughout the script (when applicable). I don't think you'll run into any problems if you use extglob throughout the script.

Comments