Bash function to wrap program and sometimes append argument

argumentsbashfunctiongrep

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
}

Best Answer

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.

Related Question