In my .bash_aliases I have defined a function that I use from the command line like this:

search -n .cs -n .cshtml -n .html SomeTextIWantToSearchFor /c/code/website/ /c/stuff/something/whatever/

The function builds a grep command that pipes the result to another grep command (unfortunately convoluted because I am stuck on an old version):

search() {
    local file_names opt OPTARG OPTIND pattern

    while getopts ":n:" opt; do
        case $opt in
                file_names+=( "$OPTARG" )
    shift $((OPTIND-1))


    if (( ${#file_names[@]} > 0 )); then
        file_names=${file_names// /':\|'}:

        grep -I -r "$pattern" "$@" | grep "$file_names"
        grep -I -r "$pattern" "$@"

I have defined another function that calls this function:

search-some-set-of-files() {
    local file_names directories

    file_names=( "-n page1.cshtml" "-n page2.cshtml" "-n page3.cshtml" )

    directories=( "/c/code/website/" "/c/stuff/something/whatever/" )

    search "${file_names[@]}" "$@" "${directories[@]}"

From the command line, I call this function like this:

search-some-set-of-files SomeTextIWantToSearchFor

For some reason, the results include every single file in the target directories. i.e., the results are not filtered by grep according to the file names I specified.

If I change the last line of the search-some-set-of-files function to echo the command, I get this:

$ search-some-set-of-files SomeTextIWantToSearchFor
search -n .cs -n .cshtml -n .html SomeTextIWantToSearchFor /c/code/website/ /c/stuff/something/whatever/

Which is exactly what I want. If I copy that command (or type it verbatim) into the command line, the results are as they should be.

If I enable debugging mode (set -x), I can see that each argument is being quoted separately by the shell:

$ search-some-set-of-files SomeTextIWantToSearchFor
+ search-some-set-of-files SomeTextIWantToSearchFor
+ local file_names directories
+ file_names=("-n page1.cshtml" "-n page2.cshtml" "-n page3.cshtml")
+ directories=("/c/code/website/" "/c/stuff/something/whatever/")
+ search '-n page1.cshtml' '-n page2.cshtml' '-n page3.cshtml' SomeTextIWantToSearchFor /c/code/website/ /c/stuff/something/whatever/
+ return
+ etc...

So I think the problem lies in how the arguments are being passed to the search function. How do I fix this?

1 Answer 1


Your problem is the second grep:

... | grep "$file_names"

When you call your function, the space between the -n and the file name (-n page1.cshtml) is included in the $file_names array. Then, the substitution:

file_names=${file_names// /':\|'}:

Will add an extra :\| at the beginning of the string because of the leading space. So, your second grep command is actually:

... | grep ":\|page1.cshtml:\|page2.cshtml:\|page3.cshtml:"

As a result, grep matches all lines since all result lines will include filename: and that is matched by :.

So, an easy solution would be to remove the spaces:

file_names=( "-npage1.cshtml" "-npage2.cshtml" "-npage3.cshtml" )

Everything should then work as expected.

  • I'm not sure I understand why, but it works. Thanks :) Commented Nov 10, 2014 at 21:40
  • Why did you escape the double quotes in ... | grep \"$file_names\""? That line's not in my code. I thought maybe you meant that's what I should do but then I have an unterminated string. Commented Nov 10, 2014 at 21:43
  • @koveras sorry, the quotes were from a copy paste of my debugging, ignore them. It workd because now there is no leading space in your file names so your substitution is not adding an extra : to the beginning of the file list. Your substitution loooks for spaces and that inckudes the one at the start. So grep was loing for : and all lines match that.
    – terdon
    Commented Nov 10, 2014 at 22:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .