Bash script to search file contents (by file extension) for the specified search term. Uses grep under the hood.

#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2086,SC2155,SC2001,SC2048

#
# Search file contents (by file extension) for the specified search term.
#
# grep options:
#
#   -i  Perform case insensitive matching.
#   −r  Recursively search subdirectories listed.
#   −I  Ignore binary files.
#   −s  Nonexistent and unreadable files are ignored (i.e. their error messages are suppressed).
#   -l  Only the names of files containing selected lines are written to standard output.
#
# Author.....: Jon LaBelle
# Date.......: November 22, 2018
# Homepage...: <https://jonlabelle.com/snippets/view/shell/search-file-content>
#

set -e
set -o pipefail

readonly SCRIPT_NAME=$(basename "${0}")

SEARCH_TERM=
FILE_EXTENSION=
FILES_WITH_MATCHES=false
VERBOSE=false

log_verbose() {
    if [ "$VERBOSE" = "true" ]; then
        echo ${1}
    fi
}

show_usage() {
    echo "Usage: ${SCRIPT_NAME} -t <term> -e <file_extension> [options]"
    echo
    echo "Search options:"
    echo
    echo "  -t, --search-term         <term>        the term to search in files for (case insensitive)."
    echo "  -e, --file-extension      <extension>   only files with this extension will be searched (w/out leading dot)."
    echo "  -l, −−files-with-matches                only print the matched (relative) file path to stdout."
    echo "                                          paths are listed only once per file searched."
    echo
    echo "Other options:"
    echo
    echo "  -v, --verbose                           useful for debugging and seeing what's going on under the hood."
    echo "  -h, --help                              show this message and exit."
    echo
    echo "Examples:"
    echo
    echo "  To search C# files containing the term 'thread':"
    echo "  $ ${SCRIPT_NAME} -t thread -e cs"
    echo
    echo "  To search C# files containing the term 'thread' and print debug information (-v):"
    echo "  $ ${SCRIPT_NAME} -v -t thread -e cs"
    echo
    echo "  To search C# files containing the term 'Thread()' and only print file name matches (-l):"
    echo "  $ ${SCRIPT_NAME} -t 'Thread()' -e cs -l"
    echo
}

search() {
    log_verbose "> Searching '${FILE_EXTENSION}' files for '${SEARCH_TERM}' in '$(pwd)'..."
    log_verbose ""

    if [ "$FILES_WITH_MATCHES" = "true" ]; then
        find . -iname '*.'${FILE_EXTENSION} -type f -exec grep -i -r -I -s -l ''${SEARCH_TERM}'' '{}' \;
    else
        find . -iname '*.'${FILE_EXTENSION} -type f -exec grep -i -r -I -s ''${SEARCH_TERM}'' '{}' \;
    fi
}

main() {
    if [[ "$1" = "-h" || "$1" = "--help" || "$1" = "help" || "$1" = "--version"  ]]; then
        show_usage
        exit 0
    fi

    while (( "$#" )); do
        if [[ "$1" = "-t" || "$1" = "--search-term" ]]; then
            log_verbose "> Setting search term filter to: '$2'"
            SEARCH_TERM=$2
        elif [[ "$1" = "-e" || "$1" = "--file-extension" ]]; then
            log_verbose "> Setting file extension filter to: '$2'"
            FILE_EXTENSION=$2
        elif [[ "$1" = "-v" || "$1" = "--verbose" ]]; then
            VERBOSE=true
            log_verbose "> Verbose mode enabled."
        elif [[ "$1" = "-l" || "$1" = "−−files-with-matches" ]]; then
            FILES_WITH_MATCHES=true
            log_verbose "> Only file matches will be printed to stdout."
        fi
        shift
    done

    if [[ -z "$FILE_EXTENSION" || -z "$SEARCH_TERM" ]]; then
        echo "Values for both '--search-term' and '--file-extension' are required."
        echo "Type '${SCRIPT_NAME} --help' for help information, including usage and examples."
        exit 1
    fi

    search $FILE_EXTENSION $SEARCH_TERM
    exit 0
}

if [ "$#" -eq 0 ]; then
    show_usage
    exit 1
else
    main $*
fi