#!/usr/bin/env bash case "$0" in ( *egrep* ) grep="egrep" ;; ( *fgrep* ) grep="fgrep" ;; ( * ) grep="grep" ;; esac if [[ "$#" = "0" || "$1" = "-h" || "$1" = "--help" ]]; then cat <<-EOF usage: $0 [grep-args] [<.ext> ...] [ ...] Will use "grep" to search recursively through the given files or directories, which defaults to ".". Ignores ".git" ".svn" "CVS" directories by default. Use "--ignore=x,y,z" to set the ignore list, "--ignore=+x,y,z" to add to the list, and "--ignore=-x,y,z" to remove from it. If an extension is given (a ".[^/]+" argument, possibly with (quoted) globs), then only files of these extension will be grepped. (Use "./.foo" for a path that begins with a a ".".) If named as something with "egrep" or "fgrep", will use these for searching. EOF exit fi grep_opts=($GREP_OPTIONS) unset GREP_OPTIONS pattern="" find_opts=() ignore=",.git,.svn,CVS," do_ignore() { if [[ "$1" = "+"* ]]; then ignore+="${1#+}," elif [[ "$1" = "-"* ]]; then for i in ${1//,/ }; do ignore="${ignore//,$i,/,}"; done else ignore=",$1," fi } while getopts "VEFGPe:f:ivwxycLlm:oqsbHhnTuZA:B:C:aD:d:IrRUz-:" name; do if [[ "$name" = "?" ]]; then exit 1 elif [[ "$name" = "-" ]]; then if [[ "$OPTARG" = "ignore" ]]; then do_ignore "$2"; shift elif [[ "$OPTARG" = "ignore="* ]]; then do_ignore "${OPTARG#ignore=}" else echo "unknown long option: $OPTARG" 1>&2; exit 1 fi elif [[ "$name" = "e" ]]; then pattern="$OPTARG" elif [[ "${OPTARG+x}" = "x" ]]; then grep_opts+=("-$name" "$OPTARG") else grep_opts+=("-$name") fi done shift $(( OPTIND - 1 )) if [[ -z "$pattern" ]]; then if [[ "$#" = "0" ]]; then echo "$0: missing pattern" 1>&2; exit 1 else pattern="$1"; shift fi fi fst=1 for i in ${ignore//,/ }; do if [[ -z "$i" ]]; then continue; fi if (( fst )); then find_opts+=("("); else find_opts+=("-o"); fi find_opts+=("-name" "$i"); fst=0 done if (( !fst )); then find_opts+=(")"); fi find_opts+=("-prune" "-o") find_opts+=("-type" "f") # add extensions to look for if specified fst=1; rx="^[.][^/]+" while [[ "$1" =~ $rx ]]; do if (( fst )); then find_opts+=("("); else find_opts+=("-o"); fi find_opts+=("-name" "*$1"); shift; fst=0 done if (( !fst )); then find_opts+=(")"); fi # If there is a single null argument from an emacs `grep', drop it if [[ "$#" = "1" && "$TERM" = "emacs-grep" ]]; then if [[ "$1" = "/dev/null" ]]; then shift; fi fi # use "." if there are no arguments if [[ "$#" = "0" ]]; then set "."; fi # check that all of the rest are existing paths for i; do if [[ ! -e "$i" ]]; then echo "$0: missing path: $i" 1>&2; exit 1; fi done # if not a single path or if a directory, add -H to show file names # (before other flags so it can be can overriden) if [[ "$#" != "1" || -d "$1" ]]; then grep_opts=("-H" "${grep_opts[@]}"); fi # print paths with NUL separators, drop "./" if the only place is "." if [[ "x$*" = "x." ]]; then find_opts+=("-printf" '%P\0') else find_opts+=("-print0") fi # See also the values of emacs vars: # grep-find-ignored-directories, grep-find-ignored-files find "$@" "${find_opts[@]}" \ | sort -z \ | xargs -0 "$grep" "${grep_opts[@]}" -e "$pattern" -- 2>&1 \ | sed 's/\(.\{1999\}\).*/\1.../' # <- trim huge lines