1

In one of my programs I call a bash command to remove all files in the directory, but some:

bash -c
shopt -s extglob
shopt -s nullglob
rm /path/to/directory/!("file1" | "file2")

Which for what I understand mean "Use bash on this command, use the extended pattern, remove all files but those"

It works good, but deletes every file but the ones put in the command.

What I would like is to remove all .mp4 and .zip in the directory but the ones I put in the command.

I think I'll have to add *.mp4 and *.zip somewhere in the command, but I have no idea where.

EDIT: For clarity, what I need is "Delete all .mp4 and .zip files, but keep thoses"

  • have you tried rm /path/to/directory/{*.zip,*.mp4}? – Anthon Nov 15 '13 at 9:59
  • Bashism... Bleah. Use find and learn how to make it portable between various system. Why would one need to use bash for such simple goal? – jirib Nov 15 '13 at 10:00
  • I am trying with rm /path/to/directory/{*.zip, *.mp4, !("file1" | "file2") – DrakaSAN Nov 15 '13 at 10:08
  • @Anthon I get rm: missing operand – DrakaSAN Nov 15 '13 at 10:26
  • @Jiri: Because the app is not meant to be portable. Is there other way to delete a file on raspbian? – DrakaSAN Nov 15 '13 at 10:27
1

The pattern !("file1" | "file2") matches any file except file1␣ or ␣file2 where is a space. The way you quoted it is misleading: it's equivalent to !(file1 | file2) or !("file1 "|" file2)`.

There's no way in bash to combine a negative pattern with a positive pattern to say “all of these except for those”. In zsh, you can write

setopt extended_glob
rm /path/to/directory/*.(mp4|zip)~(keep*|1234.zip)

to remove this.mp4 and that.zip but not keep-this.mp4 or 1234.zip.

In bash, you can set the GLOBIGNORE variable to exclude certain patterns from all matches.

shopt -s extglob
GLOBIGNORE='keep*:1234.zip'
rm /path/to/directory/*.(mp4|zip)
unset GLOBIGNORE

Alternatively, you can use find, which lets you build arbitrarily complex boolean expressions.

find /path/to/directory \( -name '*.mp4' -o -name '*.zip' \) ! -name 'keep*' ! -name '1234.zip' -delete

Note that find recurses into subdirectories. If this isn't desired, add -maxdepth 1 after /path/to/directory.

If your find doesn't understand -maxdepth (it's common but not standard), use -type d -prune -o instead. If your find doesn't understand -delete (same remark), use -exec rm {} + instead.

1

There are several ways:

  • Use find (portable as it does not need specific shell [bash])
find . -maxdepth 1 ! -name "exclude" -delete

or in case you have several patterns:

find . -maxdepth 1 ! \( -name "exclude" -o -name "exclude2" \) -exec rm -f {} \+
  • Use GLOBIGNORE variable (bash only)
export GLOBIGNORE=exclude:exclude1:mask*

That will exclude mentioned files and masks from globing. After that you can specify rm your_pattern

You can try it before with ls

  • missing closing bracket – jirib Nov 15 '13 at 10:01
  • GNUism. Please be more general and use either 'exec rm' or 'xargs rm'. Thanks. – jirib Nov 15 '13 at 10:03
  • I m trying it right now, but it seems it will find all files but thoses I exclude, what I need is to find all .mp4 and .zip but thoses I want to exclude. – DrakaSAN Nov 15 '13 at 10:36
  • For find, where is supposed to be the )? (several pattern) – DrakaSAN Nov 15 '13 at 10:39
  • @DrakaSAN you can use something like find . -name *.ext ! -name 1.ext – rush Nov 15 '13 at 11:59
0

I would you find. However your problem is the spaces.

rm /path/to/directory/!("file1" | "file2")

is the same as rm "/path/to/directory/!(file1" "|" "file2)"

not rm "/path/to/directory/!(file1|file2)"

read up on the shell quoting rules.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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