This is an example of my filesystem:

* /code/
    * internal/
        * dev/
        * main/
    * public/
        * dev/
        * main/
        * release/
    * tools/

/code/internal/dev/, /code/public/dev/ and /code/tools/ contain subdirectories for multiple projects. I work almost exclusively in the dev branches of /code/internal/ and /code/public/, and often I want to search for a text string in those directories along with /code/tools/ (which has no branches). In these instances I run a command like this:

    grep -I -r FooBar /code/internal/dev/ /code/public/dev/ /code/tools/

Additionally, sometimes I am only interested in certain file types. Then the command becomes:

    grep -I -r FooBar /code/internal/dev/ /code/public/dev/ /code/tools/ | grep .c:\|.h:

I usually forget this command between usages and end up having to relearn it. To alleviate that problem, I created a script - which I would appreciate feedback on :)

    search() {
        local t
        local OPTIND
        local pattern
        local files
        local types
        
        if [ $1 = --help ]; then
            echo "Usage: search [OPTION] ... PATTERN [FILE] ..."
            echo "Search for PATTERN in each FILE."
            echo "Example: search -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/"
            echo
            echo "Output control:"
            echo "  -t    limit results to files of type"
            return
        fi
        
        while getopts ":t:" opt; do
            case $opt in
                t) t=(${t[@]} $OPTARG);; # create an array
            esac
        done
        shift $((OPTIND-1))
        
        pattern=$1
        files=${@:2}
        
        if [ -n "$t" ]; then
            # cast the array to a string
            types=${t[@]}
            
            # convert the string to a pattern usable by grep
            # example: "c h" becomes ".c:\|.h:"
            types=.${types// /':\|.'}:
            
            grep -I -r $pattern $files | grep $types
        else
            grep -I -r $pattern $files
        fi
    }


With this and a couple more shortcut scripts, I can (relatively) quickly find anything I'm looking for:

    search-all-code() {
        search $@ /code/internal/dev/ /code/public/dev/ /code/tools/
    }

Aside: I realize some versions of grep support `--include` and `--exlude` options, but the version of grep I'm stuck with doesn't.

#Edit

This is a compromise between what I had originally and [glenn's suggestion](http://codereview.stackexchange.com/a/49111/29587) using the find command (which is much, much slower unfortunately):

    search() {
        local file_types grep_cmd opt OPTARG OPTIND pattern start_time stop_time usage
        
        start_time=$(date +%s)
        
        usage="Usage: search [OPTION] ... PATTERN [FILE] ...
    Search for PATTERN in each FILE.
    Example: search -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

    Output control:
      -t    limit results to files of type"
        
        if [[ $1 == --help ]]; then
            echo "$usage"
            return
        fi
        
        file_types=()
        while getopts ":t:" opt; do
            case $opt in
                t)
                    file_types+=("$OPTARG")
                    ;;
                ?)
                    echo "$usage"
                    return
                    ;;
            esac
        done
        shift $((OPTIND-1))
        
        if (( $# == 0 )); then
            echo "$usage"
            return
        fi
        
        pattern="$1"
        shift
        
        if (( $# == 0 )); then
            echo "$usage"
            return
        fi
        
        if (( ${#file_types[@]} > 0 )); then
            file_types="${file_types[@]}"
            file_types=.${file_types// /':\|.'}:
            
            grep -I -r "$pattern" "$@" | grep "$file_types"
        else
            grep -I -r "$pattern" "$@"
        fi

        stop_time=$(date +%s)
        echo "Elapsed (seconds):" $(( stop_time - start_time ))
    }

I'm not sure if I need to declare all those variables locally at the top of the function, but it seems like a good precaution.