Grep, else print message without matches

In a bash script, I have a list of lines in a file that I want to grep and then output to the standard version, which is easiest to do when reading:

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

However, I would like to tell the user if the strings were not matched with grep. I know that this can be done by updating the variable inside the loop , but it seems that a much more elegant approach (if possible) is to try reading the line in the loop until there is no output, an error message may 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 case, the read command is incorrect, and I was not sure about the other way of the query phrase. Is it possible that this approach is possible, and if not, is there a more suitable solution to the problem?

+5
source share
3 answers

You don't need a loop at all if you just want to display a message when there is no match. Instead, you can use the grep return code. A simple if would suffice:

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

This will display the results of grep matches (given this default behavior of grep) and display an error message if this is not the case.

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

 grep "regex" "filepath" || echo "no match" >&2 
+13
source

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

 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 (and not just show on stdout), 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 the use of a pipeline or subshell so that any variable assignments made in the loop are accessible to the rest of the script.

Also, I'm not sure if this is due to what you are trying to do at all, but grep has the ability to load templates from a file (one per line). It is called as follows:

 grep search_target -f pattern_file.txt 
+1
source

John Kugelman’s answer is correct and concise, and you must accept it. I am addressing your syntax question here for completeness only.

You cannot use ${read line} to do read - the binding syntax actually means (vaguely) that you want the value of a variable whose name contains a space. You may have fired for $(read line) , but in fact, the correct way to write a until loop will be longer in lines

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

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

It is worth emphasizing that the reason you need a separate do is because you can have several commands 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, this is much more mysterious than you will ever need. (And in this particular case, you'd probably want to actually want an if rather than a while .)

As noted earlier, there is no reason to use a cycle like this, but I wanted to understand the question of how to write such a cycle when you really want it.

+1
source

Source: https://habr.com/ru/post/1204680/


All Articles