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
mcocdawc
  • 177
  • 9
  • 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 05 '15 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 05 '15 at 20:36
  • I'm curious. Why are you using eval? Also, find has a -delete command. – muru Oct 05 '15 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 05 '15 at 20:52
  • To be on the safe side, I'd use an array to construct the condition list for find. An example: http://paste.ubuntu.com/12692155/ – muru Oct 05 '15 at 21:00
  • Thank you so much for your help. I have one last question regarding the -delete option. Sincefind also searches in sub directories the safest way was to also include -maxdepth 1? – mcocdawc Oct 05 '15 at 21:37
  • if you don't want to go into subdirectories, then you need -maxdepth 1, but if that's really the case, then you should just use terdon's solution. – muru Oct 05 '15 at 21:39
  • Yes, but an even safer way is not to use find for this at all :) find is rarely the best tool for the job when you don't want recursion. – terdon Oct 05 '15 at 21:39

1 Answers1

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})
terdon
  • 100,812