2

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?

4
  • *.sh looks more like a globbing pattern with * being the wildcard character rather than regex.
    – phk
    Mar 9, 2017 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, 2017 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, 2017 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, 2017 at 9:42

2 Answers 2

3

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.

1

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"
2
  • Putting single quotes around arguments doesn't make them safe to use in eval, you also need to handle single quotes inside those arguments. Jun 4, 2021 at 5:38
  • Note that -not is not standard, the standard equivalent is ! Jun 4, 2021 at 5:38

You must log in to answer this question.

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