33

I am trying to have my Makefile echo text without the trailing new line, but am unable to. I am experiencing the behavior on OS X (on Linux everything works as expected).

Makefile

a:
    @echo -n "hello"

b:
    @echo -n hello

c:
    @/bin/echo -n "hello"

Output:

$make a
-n hello
$make b
hello$make c
hello$

In other words, the make a is broken. What exactly is happening? Is make using a built-in echo? Clearly the presence of the double quotes changes the behavior, but why?

Update

As discovered by @chepner, using the full path to /bin/echo in the makefile understands the -n flag correctly.

Community
  • 1
  • 1
Chris
  • 2,786
  • 1
  • 28
  • 31

3 Answers3

35

The problem comes from the unfortunate interaction of two facts.

First, make has two modes of operations depending on the complexity of the recipe to be run:

  • If the command is easy, make will directly run the recipe with its builtin commands. This is what happens in your b case.
  • If the command is complex, make will spawn a shell to interpret and run the recipe. This is what happens in your a case.

Second, make uses /bin/sh as a shell but the functionality of /bin/sh is implemented differently on Mac OS X and Linux:

  • On Mac OS X, the functionality of /bin/sh is implemented by bash. Also on Mac OS X, bash is compiled with --enable-strict-posix-default. One consequence of this flag is that the echo command does not understand the -n flag.
  • On Linux, the functionality of /bin/sh is implemented by dash which is less strict with respect to POSIX specification. Therefore the flag -n is implemented in the echo command.

BTW, the Makefile buitlin echo command understands the -n flag which explains why the b case always works.

The clean and portable way of fixing your problem is to replace your @echo -n recipes with @printf recipes.

Nicolas Dudebout
  • 9,172
  • 2
  • 34
  • 43
  • Somewhat related: on Mac Lion 10.7 at least, root's default shell is /bin/sh not /bin/bash. Best to use 'chsh' command to change this to /bin/bash (no arguments necessary) so that 'echo -n' works in .bashrc etc. – RichVel Mar 28 '13 at 13:43
29

Something about the quotes confuses make. Your code behaves the same for me, but the following works as expected:

help:
        @echo -n Shouldn\'t print a newline

Hardcoding the path to the executable also works:

help:
        @/bin/echo -n "Shouldn't print a newline"

The Mac OS X man page for echo, while discussing the existence of shell built-in echos, mentions that the echo of sh(1) does not support the -n option, but that fails to explain (to me, anyway) why my first alternative works.


Confirmation that make is using sh to execute the commands by default. Make manual 5.3.2 Choosing the Shell specifies this hehavrior:

The program used as the shell is taken from the variable SHELL. If this variable is not set in your makefile, the program /bin/sh is used as the shell. The argument(s) passed to the shell are taken from the variable .SHELLFLAGS. The default value of .SHELLFLAGS is -c normally, or -ec in POSIX-conforming mode.

In

SHELL = bash
help:
        @echo -n "Shouldn't print a newline"
        @echo -n Shouldn\'t print a newline

both echo statements behave the same (no newlines printed). So without that variable, we have bash pretending to be sh, but evaluating the two lines differently. Question 1: why? Question 2: is the second line the native bash echo or /bin/echo, rather than the emulated sh echo?

Jingguo Yao
  • 7,320
  • 6
  • 50
  • 63
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Actually, I had just realized this and was changing my question to reflect it. But why exactly is `make` confused...and how could I make it explicit to `make` that I want to pass the -n flag to echo (without the explicit path?) Is make using some sort of built in echo? – Chris Jul 26 '12 at 18:41
  • The entire recipe is executed using the shell specified by `$SHELL`, or `/bin/sh` if that variable isn't set. So ordinarily, if the shell in question has a built-in, it will use that unless the path is hardcoded. The odd thing here is that despite `$SHELL` being set to '/usr/local/bin/bash' (for me, anyway), the problem line seems to executed using the `sh` built-in, not the `bash` built-in. – chepner Jul 26 '12 at 18:50
  • 1
    I was having issues with the `-e` flag earlier, so it's all flags that confuses make for some odd reason... – Anonymous Penguin Oct 11 '14 at 17:20
3

echo is a bash shell builtin, but when you run it from makefile, it is the program version

pizza
  • 7,296
  • 1
  • 25
  • 22
  • 1
    `-n` is supported by both the built-in and the standalone program. – chepner Jul 26 '12 at 18:25
  • 2
    The program version (/bin/echo) actually supports -n, so if the Makefile was using /bin/echo, it would work correct. – Chris Jul 26 '12 at 18:28