I'm interested in a general solution, but my specific example problem is writing a .bashrc
function that wraps grep
and appends a file path to the command if missing. Basically, any time grep would wait on stdin I instead want it to search a specific file. My problem is how to tell (in the wrapper) whether the final argument is a path to be searched versus e.g. the search pattern.
$ ls
example_file.txt
$ grep -someopts 'somestring' 'example_file.txt'
Pretend that -someopts
are in fact arbitrary valid options to grep.
Is the final argument a pattern (to be searched for) or a file (to be searched)?
If somestring
is a parameter to one of the -options, then example_file.txt
is the pattern to search for, and grep will wait on stdin. Otherwise, somestring
is the pattern to search for and example_file.txt
will be searched.
In the former case, I want to append my own file to be searched on the end of the command, but I can't detect said case without false-positives. The only way seems to be for the wrapper to consider every argument that grep could take.
Here is my wrapper function (where check_has_path
is what I need to implement):
function grep_wrapped() {
if check_has_path ; then
grep "$@"
else
grep "$@" '/default/filepath.txt'
fi
}
-
What about the case where grep is working on stdin?Jeff Schaller– Jeff Schaller ♦2017年10月06日 15:07:56 +00:00Commented Oct 6, 2017 at 15:07
-
@Jeff Schaller I will not be piping stdin to the wrapper. I will either be using it with a specified search path/file (in which case for the purposes of this example it would be equivalent to just calling grep), or not (in which case the default search path/file should be searched)benxyzzy– benxyzzy2017年10月06日 15:13:06 +00:00Commented Oct 6, 2017 at 15:13
2 Answers 2
Approach: Separate the command line options from the pattern and possible filenames on the command line, then count the command line arguments that are not options. If there's more than one, run the command as is, otherwise tag on your file.
In bash
:
mygrep () {
local -a opts
while [ "$#" -gt 0 ]; do
case "1ドル" in
--) opts+=( "1ドル" ); shift; break ;;
-*) opts+=( "1ドル" ) ;;
*) break
esac
shift
done
if [ "$#" -gt 1 ]; then
grep "${opts[@]}" "$@"
else
grep "${opts[@]}" "$@" "/my/file"
fi
}
The function separates the command line options from the rest of the command line and checks whether there are more than one non-option command line argument or not (i.e. something other than a pattern). If there isn't, your file is tagged onto the end of the command.
This does not work if you use options with option-arguments (e.g. -e PATTERN
), so it's somewhat flawed. Maybe it can serve as a starting point for someone else?
From comments it is clear that the user is not interested in running grep
at all, but rather in searching for files with a particular extension.
The following shell function does that:
extfind () {
local ext="1ドル"
if [ -z "$ext" ]; then
echo 'Missing extension' >&2
return 1
fi
shift
local dir="${1:-$HOME}"
find "$dir" -type f -name "*.$ext"
}
This function would be used as
$ extfind txt
to find all files whose names ends with .txt
in or under the home directory, or
$ extfind "[hc]" /usr/src
to find all files whose names end with either .h
or .c
in or under the /usr/src
directory.
-
Great, except you have an explicit path in your last
grep
. The question appears to be that he wants the last argument of the previous command to be the "default". I.e., addlocal lastfile="$_"
to the top of the function, and change"/my/file"
to read"$lastfile"
Rich– Rich2017年10月06日 17:47:10 +00:00Commented Oct 6, 2017 at 17:47 -
1@Rich Sorry, but I can't see anything about using the last argument of the previous command in the question. In the OP's own code, there is an explicit and literal path too. Maybe it's "I want to append my own file to be searched on the end of the command" that you're interpreting like that? I read that as "I want to apply
grep
to a specific file".2017年10月06日 18:02:01 +00:00Commented Oct 6, 2017 at 18:02 -
Right! Lack of comprehension on my part. I misinterpreted the first two lines as a single line, i.e.
$ ls example_file.txt
. Plus, I guess it doesn't make sense for a default filename to be hardcoded into such a general-purpose command asgrep
. Mind, it doesn't make sense to avoid typing Ctrl-D, cursor-up, Alt-. either!Rich– Rich2017年10月06日 19:40:18 +00:00Commented Oct 6, 2017 at 19:40 -
My example is simplified; I'm trying to write a custom grep wrapper for searching a specific file extension. Often I'll want to find one such file somewhere in my home directory, in which case I'd like to omit the filepath. When I include a filepath or directory however, I want that to be searched instead of my home. All other behaviour and functionality should be as grep. Specifically, I may use any options. If a general solution exists it would greatly extend my use of wrappersbenxyzzy– benxyzzy2017年10月07日 14:25:01 +00:00Commented Oct 7, 2017 at 14:25
-
@benxyzzy So, you're actually not interested in grepping the contents of a file, but rather to search for files with a particular extension? This is not what
grep
does, but it's trivial to write a shell function that usesfind
to do this.2017年10月07日 14:27:25 +00:00Commented Oct 7, 2017 at 14:27
You could go with a sort of pythonesque try it and if there is an exception do something else. Something like:
function grep_wrapped(){
grep "$@" <&-
local rc=$?
if [ $rc = 2 ] # probably a read error from closed stdin
then grep "$@" /default/filepath.txt
else return $rc
fi
}
It leaves all the work to grep. This will have the side-effect of the error message
grep: (standard input): Bad file descriptor
when you have the exit code 2 from grep because stdin was closed. You can obviously redirect stderr to capture this to file or variable, and print it if the return code was 0 or 1.