1

I would like to convert and execute

if egrep -r 'my_pattern' ./template_builder
then exit 1
elif egrep -r 'my_second_pattern' ./template_builder
then exit 1
fi

in a Makefile, without success for now.

To build this:

cd /tmp;
mkdir template_builder;
echo "not_pattern" >> ./template_builder/test.txt
# Do the command at the top, nothing happens
echo "my_pattern" >> ./template_builder/test.txt
# Do the command at the top, terminal stops

touch Makefile 

In a Makefile, I thought this would work :

check:
    if egrep -r 'my_pattern' ./template_builder
    then exit 1
    elif egrep -r 'my_second_pattern' ./template_builder
    then exit 1
    fi
make check
if egrep -r 'my_pattern' ./template_builder
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [template] Error 2

How can I fix this?

tripleee
  • 175,061
  • 34
  • 275
  • 318
Dimitri Leurs
  • 590
  • 5
  • 19
  • 1
    maybe [related](https://stackoverflow.com/questions/16315089/how-to-get-exit-status-of-a-shell-command-used-in-gnu-makefile), `||` maybe changed to `&&` – Nahuel Fouilleul Jul 27 '21 at 14:55

3 Answers3

2

Your attempt was not far from working!

Add backslashes at the end of every line, and ;s as explicit command separators (and of course use real tabs instead of the 8-space indents below):

check:
        if egrep -r 'my_pattern' ./template_builder; \
        then exit 1; \
        elif egrep -r 'my_second_pattern' ./template_builder; \
        then exit 1; \
        fi
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    @DimitriLeurs, see this running at https://replit.com/@CharlesDuffy2/VirtuousAdoredMuse#main.sh -- the only errors it throws is because the sandbox contains no `template_builder` file. (I've since edited and it no longer throws even that). – Charles Duffy Jul 27 '21 at 15:49
  • it worked when putting everything on one line – Dimitri Leurs Jul 27 '21 at 16:00
  • 1
    Sure, but the point of backslashes at the ends of your lines is that you don't need to do even that. (Mind, the backslash does need to be at the very end of the line; if you had a space after it, that would break it so it would no longer behave as described). See the linked repl -- it _does_ have multiple lines, and it _does_ work exactly as-is. – Charles Duffy Jul 27 '21 at 16:03
1

If I understand you correctly, if the directory template_builder located in /tmp does not contain a file matching the string 'my_pattern' or 'my_second_pattern', you want to exit from make with an error code.

You can achieve this with this rule in Makefile:

check:
        egrep -r -v 'my_pattern' /tmp/template_builder || egrep -r -v 'my_second_pattern' /tmp/template_builder

Explanation: the first egrep is going to return an error in the case he finds a match. Due to the presence of the || operator, the second egrep will be invoked. The result of this second command will be the result that make will see. If it returns an error, the execution of make is aborted, which seems to be the behaviour you are expecting.

Caution: I edited my answer. The right boolean operator is || and not &&.

Pierre François
  • 5,850
  • 1
  • 17
  • 38
1

As others have already noted, make runs each separate line in a recipe in a new shell subprocess. (For the record, it uses sh out of the box, not Bash.) The trivial fix is to add a backslash to escape the newline at the end of each line which should be executed in the same shell as the next one. (You need to add a semicolon as well in some places, like before then and else and fi.) But you really want to refactor to use the facilities and idioms of make.

The default logic of make is to terminate the recipe if any line fails. So, your code can be reduced to simply

check: template_builder
    ! egrep -r 'my_pattern' $<
    ! egrep -r 'my_second_pattern' $<

The explicit exit 1 is not necessary here (negating a zero exit code produces exactly that); but if you wanted to force a particular exit code, you could do that with

    egrep -r 'my_pattern' $< && exit 123 || true

Modern POSIX prefers grep -E over legacy egrep; of course, with these simple patterns, you can use just grep, or even grep -F (née fgrep).

Moreover, if you want to search for both patterns in the same set of files, it's much more efficient to search for them both at once.

check: template_builder
    ! egrep -e 'my_pattern' -e 'my_second_pattern' -r $<

... or combine them into a single regex my_(second_)?pattern (which requires egrep / grep -E).

Notice also how I factored out the dependency into $< and made it explicit; but you probably want to make this recipe .PHONY anyway, so that it gets executed even if nothing has changed.

(You can't directly copy/paste this code, because Stack Overflow stupidly renders the literal tabs in the markdown source as spaces.)

tripleee
  • 175,061
  • 34
  • 275
  • 318