1

My find looks really simple:

find . -type f

I'm trying to figure out how to exclude certain files or directories, depending on the program argument line.

bash myscript.sh -excl a b c d 

Where a, b, c, d or any next argument of excl option is a regex referring to certain files or directories I want to exclude.

So if I called the program this way:

bash myscript.sh -excl *.sh somedir

I simply want find to ignore somedir directory and all files with .sh extension. Is this possible in bash?

  • *.sh looks more like a globbing pattern with * being the wildcard character rather than regex. – phk Mar 9 '17 at 9:27
  • The number of arguments can differ? How should the script distinguish when you are referring to a directory and when to a file name pattern? – phk Mar 9 '17 at 9:29
  • First of all option -excl a b c d is not a posix style. Either --exclude a,b,c,d or -e a,b,c,d or -e a -e b -e c -e d or you're confusing most of script's users. Secondly, it's not a bash problem at all. find is not a part of bash – kubanczyk Mar 9 '17 at 9:30
  • The bash part is to quote the arguments to prevent it from being expanded prior to matching. For the rest I would not write a script, but try something like find /path/to/dir -not -name "*.sh" -not -name somedir (untested) – Philippos Mar 9 '17 at 9:42
2

Yes, you can just add a -not -name if there is a second argument to your script:

#!/bin/bash 

targetDir="$1"
exclude="$2"

findString=" '$targetDir'"

if [[ ! -z "$exclude" ]]; then
        findString="$findString -not -name '$exclude'"
fi
eval "find $findString"

For example:

$ ls
file1  file1.sh  file2  file2.sh  file3.sh  file4.sh  file5.sh
$ foo.sh .
.
./file1
./file2
./file1.sh
./file4.sh
./file3.sh
./file5.sh
./file2.sh
$ foo.sh . '*sh'
.
./file1
./file2

If you want to be able to define multiple patterns to exclude:

#!/bin/bash 

targetDir="$1"
findString=" '$targetDir'"
shift
exclude="'$1'"
shift
for i in "$@"; do
        exclude="$exclude -a -not -name '$i'";
done
if [[ ! -z "$exclude" ]]; then
        findString="$findString -not -name $exclude"
fi
eval "find $findString"
| improve this answer | |
1

Solution using bash arrays:

#!/bin/bash

declare -a find_arguments=( -type f )

for arg; do

  find_arguments+=( ! -name "$arg" )

done

find . "${find_arguments[@]}"

If you really want regex then change -name into -regex but from your example you seem to want globbing. (BTW, -regex is not POSIX but supported by GNU find.)

Demo

touch {a,b,c}{x,y,z}
./myscript.sh 'a*' '*z'

Output:

./bx
./by
./cx
./cy

I left out the command line parsing for -excl and such since you didn't make clear how your script's command line options generally look like, if it's truly just -excl then you can simply check for it ([[ "$1" = -excl ]]) and then shift.

| improve this answer | |

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.