grep, else print message for no matches

18,607

Solution 1

You don't need a loop at all if you simply want to display a message when there's no match. Instead you can use grep's return code. A simple if statement will suffice:

if ! grep "regex" "filepath"; then
    echo "no match" >&2
fi

This will display the results of grep matches (since that's grep's default behavior), and will display the error message if it doesn't.

A popular alternative to if ! is to use the || operator. foo || bar can be read as "do foo or else do bar", or "if not foo then bar".

grep "regex" "filepath" || echo "no match" >&2

Solution 2

John Kugelman's answer is the correct and succinct one and you should accept it. I am addressing your question about syntax here just for completeness.

You cannot use ${read line} to execute read -- the brace syntax actually means (vaguely) that you want the value of a variable whose name contains a space. Perhaps you were shooting for $(read line) but really, the proper way to write your until loop would be more along the lines of

grep "regex" "filepath" | until read line; [[ -z "$line" ]]; do

... but of course, when there is no output, the pipeline will receive no lines, so while and until are both wrong here.

It is worth amphasizing that the reason you need a separate do is that you can have multiple commands in there. Even something like

while output=$(grep "regex filepath"); echo "grep done, please wait ...";
    count=$(echo "$output" | wc -l); [[ $count -gt 0 ]]
do ...

although again, that is much more arcane than you would ever really need. (And in this particular case, you would want probably actually want if , not while.)

As others already noted, there is no reason to use a loop like that here, but I wanted to sort out the question about how to write a loop like this for whenever you actually do want one.

Solution 3

As mentioned by @jordanm, there is no need for a loop in the use case you mentioned.

output=$(grep "regex" "file")
if [[ -n $output ]]; then
    echo "$output"
else
    echo "Sorry, no results..."
fi

If you need to iterate over the results for processing (rather than just displaying to stdout) then you can do something like this:

output=$(grep "regex" "file")
if [[ -n $output ]]; then
    while IFS= read -r line; do
        # do something with $line
    done <<< "$output"
else
    echo "Sorry, no results..."
fi

This method avoids using a pipeline or subshell so that any variable assignments made within the loop will be available to the rest of the script.

Also, i'm not sure if this relates to what you are trying to do at all, but grep does have the ability to load patterns from a file (one per line). It is invoked as follows:

grep search_target -f pattern_file.txt
Share:
18,607

Related videos on Youtube

seraththundhuil
Author by

seraththundhuil

PhD student in computer science and some experience as an independent developer. Greatest areas of expertise in Java, C# and Lua, though not yet an expert in any.

Updated on June 06, 2022

Comments

  • seraththundhuil
    seraththundhuil almost 2 years

    In a bash script, I have a list of lines in a file I wish to grep and then display on standard out, which is easiest done with a while read:

    grep "regex" "filepath" | while read line; do
        printf "$line\n"
    done
    

    However, I would like to inform the user if no lines were matched by the grep. I know that one can do this by updating a variable inside the loop but it seems like a much more elegant approach (if possible) would be to try to read a line in an until loop, and if there were no output, an error message could be displayed.

    This was my first attempt:

    grep "regex" "filepath" | until [[ -z ${read line} ]]; do
        if [[ -z $input ]]; then
           printf "No matches found\n"
           break
        fi
        printf "$line\n"
    done
    

    But in this instance the read command is malformed, and I wasn't sure of another way the phrase the query. Is this approach possible, and if not, is there a more suitable solution to the problem?

    • jordanm
      jordanm over 9 years
      In your first example, only the grep is needed and you get the same behavior. Is this just an for an example or is what you really want to do?
    • chepner
      chepner over 9 years
      while read line; do is the same as until ! read line; do. There's very little difference between the two; which one you use depends more on how you want to negate (or not negate) the condition than anything else.