I'm looking for a way to abstract common command:
find src app -name "*.hs" -exec grep -H "foo" \{\} \;
to a bash/zsh function like:
find $1 -exec grep -H $2 \{\} \;
Above is not a solution as I'd like to pass lists of arguments for example with a given syntax: findgrep "src -name '.hs'" "foo"
.
Here we could say that argument to grep is ever really going to be a single word and then pass the rest of argument list to find but there ought to be a more general way? Is there a way to sort of partial apply a bash function?
Thanks in advance!
In general, you cannot do that, as every individual argument is just a regular string; the shell has no distinct list type.
To pass two "groups" of arguments, you'll need to define some sort of "sentinel" that your function can recognize as such. For example,
findgrep () {
args=()
for x; do
shift
[[ $x == ::: ]] && break
args+=("$x")
done
find "${args[@]}" -exec grep -H "$@" {} \;
}
findgrep src -name ".hs" ::: foo
findgrep
will add arguments to the array args
until it sees the argument :::
, at which point it will ignore :::
but stop iterating. Now you can use args
as the first group of arguments to find
, and whatever is left in $@
as the second group.
Finding a valid sentinel could be difficult, as there's really no string that couldn't, in theory, also be a valid argument to find
. An alternative would be to use an agreed-upon global array to store one or both sets.
findgrep () {
find "${findgrepargs[@]}" -exec grep -H "$@" {} \;
}
findgrepargs=(src -name .hs)
findgrep foo
or to use a nameref to pass the name of an aribitary array to the function.
findgrep () {
declare -n findargs=$1
shift
findgrep "${findargs[@]}" -exec grep -H "$@" {} \;
}
x=(src -name .hs)
findgrep x foo