Skip to main content
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;return;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" '(')
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done
 
    find_cmd+=(')' -exec grep -I "$pattern" '{}' + ) 

    "${find_cmd[@]}"
}
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" )
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done
 
    find_cmd+=( -exec grep -I "$pattern" '{}' + )
    "${find_cmd[@]}"
}
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=(find "$@" '(')
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=($or -name "*.$type")
        or="-o"
    done
    find_cmd+=(')' -exec grep -I "$pattern" '{}' +) 

    "${find_cmd[@]}"
}
added 5 characters in body
Source Link
glenn jackman
  • 2.5k
  • 12
  • 13
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" )
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done

    find_cmd+=( -exec grep -I "$pattern" '{}' + )
    "${find_cmd[@]}"
}
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" )
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done

    find_cmd+=( -exec grep -I "$pattern" + )
    "${find_cmd[@]}"
}
search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" )
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done

    find_cmd+=( -exec grep -I "$pattern" '{}' + )
    "${find_cmd[@]}"
}
Source Link
glenn jackman
  • 2.5k
  • 12
  • 13

I'd lean towards find. I'll provide some code review type comments at the bottom, but first a rewrite:

search() {
    local extensions
    local pattern
    local find_cmd
    local OPTIND opt

    local usage=$( cat - << END
Usage: $FUNCNAME [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: $FUNCNAME -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type
END
)
    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    extensions=()
    while getopts ":ht:" opt; do
        case $opt in
            h) echo "$usage"; return ;;
            t) extensions+=("$OPTARG");;
            ?) echo "invalid option: -$OPTARG";;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "no search term provided"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "no directories provided"
        return
    fi
    # your directories to search are now the positional parameters

    find_cmd=( find "$@" )
    or=""
    for type in "${extensions[@]}"; do
        find_cmd+=( $or -name "*.$type" )
        or="-o"
    done

    find_cmd+=( -exec grep -I "$pattern" + )
    "${find_cmd[@]}"
}

Notes

  • [ $1 = --help ] will give you syntax error if $1 is blank -- you get [ = --help ] and the = operator requires 2 operands. bash's [[ ... ]] is smarter about not dropping operands just because they're empty
  • You can build up arrays bit-by-bit with arr+=("$element")
  • you want to use more double quotes for your variables. If the pattern is "hello world", then grep $pattern file1 will look for the pattern "hello" in files "world" and "file1" -- grep "$pattern" file1 will work as you expect.
  • I use (( ... )) arithmetic expression for numeric comparisons.
  • I build up the find command piece by piece and execute it with "${arr[@]}" -- that specific syntax, indexing the array with [@] and surrounding it with double quotes, is the way you'll want to expand arrays most of the time. That specific syntax will expand the array into elements, but it will keep elements containing whitespace as a single argument.
    • This includes using "$@" over $@ -- the former gives you the actual arguments given by the user, the latter gives you all the words in the arguments. A demonstration:

        set -- "foo bar" "hello world"    # set the positional parameters
        printf "%s\n" "$@" | wc -l        # print the parameters one per line 
                                          # and count the lines -- 2
        printf "%s\n" $@ | wc -l          # here you get 4
      
  • if t is an array, $t gives you the first element only.