2

I have the same problem as this question How to delete all the files/folders from the folder except few folders? several times. This is the reason why I wanted to write myself a script for a command rmnot. It should take an arbitrary number of files even with wildcards if necessary and delete anything (not recursevely) in the same directory except for those files. Typical example would be:

rmnot *tex *bib *png

My script works, but since I am inexperienced and want to learn it the proper way, is there a more elegant way to do write this script?

#!/bin/zsh

insert="-name . -or -name .."

for i in {1..$#}; do
    insert="$insert -or -name ${(P)i}"
done

insert="\( $insert \)"

eval "find -not $insert -exec rm {} \;"

PS: I have to use ZSH because of the double substitution ${(P)i} anything else would work in bash as well I think.

======Optimized Version=====

 #!/bin/bash

 insert="-name . -or -name .."

 for i; do
    insert="$insert -or -name $i"
 done

 insert="\( $insert \)"

 find -maxdepth 1 -not $insert -delete
8
  • Comments: Instead of -name . or -name .., use -mindepth 1. You don't need double substitution. Just do for i; do insert="$insert -or -name $i"; done. A for x without an in loops over the script arguments.
    – muru
    Oct 5, 2015 at 20:24
  • @muru Thank you with the tip for the loop! The -mindepth 1 option brings new problems because -or forces something to have in front. So I need to define insert= something nevertheless before the loop. Or I need some other lines to chop the -or in front away. But I think this would be over engineering.
    – mcocdawc
    Oct 5, 2015 at 20:36
  • I'm curious. Why are you using eval? Also, find has a -delete command.
    – muru
    Oct 5, 2015 at 20:43
  • I was just to happy to be able to insert options via variables ;). I changed it accordingly and added -maxdepth 1 for more security. Since the double substitution is not necessary anymore it is even bash compatible now I think.
    – mcocdawc
    Oct 5, 2015 at 20:52
  • To be on the safe side, I'd use an array to construct the condition list for find. An example: paste.ubuntu.com/12692155
    – muru
    Oct 5, 2015 at 21:00

1 Answer 1

10

You don't even need a script. If you're using bash, you can turn on extglob and give negative patterns:

$ ls
foo.avi  foo.bbl  foo.bib  foo.log  foo.png  foo.tex  foo.txt  foo.wav
$ shopt -s extglob
$ rm !(*tex|*bib|*png)
$ ls
foo.bib  foo.png  foo.tex

As explained in man bash:

   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 fol‐
   lowing sub-patterns:

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns

With zsh, the equivalent is:

setopt extended_glob
rm ^(*tex|*bib|*png)

If you still want to write a script for this, just concatenate the arguments you give it, but don't use wildcards (*), let the script add them (thanks to @Helios for suggesting a simpler version):

#!/usr/bin/env bash

## Iterate over the arguments
for i
do
    ## Add them to the pattern to be excluded
    pat+="*$i|"
done
## Activate extglob
shopt -s extglob
## Delete non-matching files
rm !(${pat})
5
  • The thing is that I do not use wildcards all the time. But I think now I got enough answers to make it properly.
    – mcocdawc
    Oct 5, 2015 at 21:41
  • and especially I see that I do not need a script: extglob is now in my .zshrc
    – mcocdawc
    Oct 5, 2015 at 21:44
  • @mcocdawc no! If you use zsh, you should add setopt extended_glob to your ~/.zshrc and use the rm ^(*tex|*bib|*png) syntax as I show in my answer. extglob is not a zsh thing, it's bash.
    – terdon
    Oct 5, 2015 at 21:45
  • it works as expected now ;)
    – mcocdawc
    Oct 5, 2015 at 21:47
  • @terdon: Impressing, I learn a lot wit this script.
    – 0x2b3bfa0
    Oct 7, 2015 at 20:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .