function gg-by-file {
local usage="Usage:
gg-by-file <file-filter> <text-pattern>
file-filter:
include1[,include2...][~exclude1[,exclude2...]]
Matching:
include : OR
exclude : OR
filename matching is case-insensitive
Examples:
gg-by-file cpp printf
gg-by-file cpp,h vector
gg-by-file parser~test Token
gg-by-file '*.cpp,*.hpp~test,temp' parse"
#
# help
#
case "$1" in
-h|--help)
printf '%s\n' "$usage"
return 0
;;
esac
if (( $# < 2 )); then
printf '%s\n' "$usage"
return 1
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Not inside a git repository."
return 1
fi
local filter="$1"
local pattern="$2"
#
# split include / exclude
#
local include="${filter%%~*}"
local exclude=""
[[ "$filter" == *'~'* ]] && exclude="${filter#*~}"
#
# validate
#
if [[ -z "$include" ||
"$filter" == *'~~'* ||
"$include" == ,* ||
"$include" == *, ||
"$exclude" == ,* ||
"$exclude" == *, ]]; then
echo "Invalid file-filter: $filter"
return 1
fi
local -a inc exc files
IFS=',' read -ra inc <<<"$include"
if [[ -n "$exclude" ]]; then
IFS=',' read -ra exc <<<"$exclude"
fi
local file file_lower kw kw_lower matched skip
#
# Single pass through git files
#
while IFS= read -r file
do
file_lower="${file,,}"
#
# include (OR)
#
matched=0
for kw in "${inc[@]}"
do
kw_lower="${kw,,}"
if [[ "$kw_lower" == *[\*\?]* ]]; then
[[ "$file_lower" == $kw_lower ]]
else
[[ "$file_lower" == *"$kw_lower"* ]]
fi || continue
matched=1
break
done
(( matched )) || continue
#
# exclude (OR)
#
skip=0
for kw in "${exc[@]}"
do
[[ -z "$kw" ]] && continue
kw_lower="${kw,,}"
if [[ "$kw_lower" == *[\*\?]* ]]; then
[[ "$file_lower" == $kw_lower ]]
else
[[ "$file_lower" == *"$kw_lower"* ]]
fi || continue
skip=1
break
done
(( skip )) || files+=("$file")
done < <(git ls-files)
if (( ${#files[@]} == 0 )); then
echo "No matching files."
return 1
fi
#
# Content search
#
git grep -nE --color=auto -- "$pattern" -- "${files[@]}"
}
3 个赞
{grep $1 $(git ls-files|grep -v $2|xargs)}
1 个赞
要这么长吗,有没有更简短一点的方案