This is an example of my filesystem:
- /code/
- internal/
- dev/
- main/
- public/
- dev/
- main/
- release/
- tools/
- internal/
/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 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.