9

On a bash prompt, one can execute diff using pseudo files:

diff <(echo test) <(echo test)

Adding this as is into a Makefile fails:

all:
        diff <(echo test) <(echo test)

The error (hint: /bin/sh points to /bin/bash on this system):

/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `diff <(echo test) <(echo test)'

What does it mean, and is there a way to still diff two outputs without using temporary files?

17

/bin/sh may be bash on your system, but when invoked as sh, bash will be running in POSIX mode (as if POSIXLY_CORRECT was defined, or it was started with --posix).

In this mode, process substitutions do not exist.

Solutions:

all:
    command1 >file1
    command2 >file2
    diff file1 file2
    rm -f file1 file2

Alternative:

all:
    bash -c "diff <(command1) <(command2)"

Or just define the Makefile variable SHELL as /bin/bash:

SHELL=/bin/bash

If you want portability, go with the first solution. If you are OK with a dependency on bash, pick the second. If you additionally don't need to care about non-GNU make implementations, use the third.


Regarding setting SHELL: The POSIX standard says that executables in Makefiles should be invoked with the system() C library function by make. This function is not guaranteed to use the SHELL environment variable (in fact, doing so is discouraged by the standard). The standard also goes to some length to say that setting the Makefile variable SHELL should not affect the environment variable SHELL. In most implementations of make that I know of, however, the Makefile variable SHELL will be used to execute the commands.

The suggestion in the Rationale for the make utility is to use bash -c:

The historical MAKESHELL feature, and related features provided by other make implementations, were omitted. In some implementations it is used to let a user override the shell to be used to run make commands. This was confusing; for a portable make, the shell should be chosen by the makefile writer. Further, a makefile writer cannot require an alternate shell to be used and still consider the makefile portable. While it would be possible to standardize a mechanism for specifying an alternate shell, existing implementations do not agree on such a mechanism, and makefile writers can already invoke an alternate shell by specifying the shell name in the rule for a target; for example:

python -c "foo"

  • Is there a way to invoke bash in the Makefile or any other solution to the diff problem without using temporary files? – Johannes Apr 7 '17 at 9:13
  • Just use two temporary files, this is more or less what the process substitution method would do under the hood anyway. – Kusalananda Apr 7 '17 at 9:15
  • 3
    You could also set SHELL to /bin/bash in the Makefile. – Stephen Kitt Apr 7 '17 at 9:22
  • 1
    @Johannes Kusalananda’s added the info to his answer, which is great since it presents a number of options and the circumstances in which they can be used. I’d rather you accept this answer... (But I appreciate the sentiment!) – Stephen Kitt Apr 7 '17 at 9:31
  • 2
    The info that using the SHELL variable is not POSIX conform was very helpful. Maybe it's still better to use bash -c. – Johannes Apr 7 '17 at 9:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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