Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

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

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

    pattern="$1"
    shift

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

        grep -I -r "$pattern" "$@" | grep "$file_names"
    else
        grep -I -r "$pattern" "$@"
    fi
}

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?

share|improve this question

1 Answer 1

up vote 6 down vote accepted

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.

share|improve this answer
    
I'm not sure I understand why, but it works. Thanks :) –  Koveras Nov 10 '14 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. –  Koveras Nov 10 '14 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 Nov 10 '14 at 22:03

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.