sffc
sffc

Reputation: 6424

How to preserve whitespace when saving command output to a Makefile variable?

I am aware of questions like this one, but my question is specifically about makefile, specifically gnumake.

I have a command that prints line breaks and other whitespace to standard out. I want to capture that output into a variable and then later print the variable to a file.

Example Makefile:

OUTPUT=${shell cowsay hello}

all:
    @echo "$(OUTPUT)" > output.txt

After running make, output.txt contains:

 _______  < hello >  -------          \   ^__^          \  (oo)\_______             (__)\       )\/\                 ||----w |                 ||     ||

I want it to retain the whitespaces and instead contain:

 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

The command I'm actually using is not cowsay, but it outputs similar whitespace and linebreaks.

Upvotes: 14

Views: 2978

Answers (4)

Mahmoud
Mahmoud

Reputation: 11461

Base64 encoding

I suggest you encode the cowsay's output as base64:

$ cowsay hello

 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


$ cowsay hello | base64 -w0
IF9fX19fX18gCjwgaGVsbG8gPgogLS0tLS0tLSAKICAgICAgICBcICAgXl9fXgogICAgICAgICBcICAob28pXF9fX19fX18KICAgICAgICAgICAgKF9fKVwgICAgICAgKVwvXAogICAgICAgICAgICAgICAgfHwtLS0tdyB8CiAgICAgICAgICAgICAgICB8fCAgICAgfHwK

Makefile

cow:
    @echo IF9fX19fX18gCjwgaGV.... | base64 -d

make cow

Now simply running make cow displays it properly (without wrong line breaks)

$ make cow

 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Upvotes: 3

bobbogo
bobbogo

Reputation: 15493

Since $(shell) strips out newlines, simply replace them with another character or pattern. Once back in make, replace that character with a newline.

Here I chose : as my newline replacement. YMMV

define \n


endef

cow := $(subst :,${\n},$(shell cowsay hello | tr \\n :))

$(error [${cow}])

Giving

$ make
Makefile:7: *** [ _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
].  Stop.

You will find it tricky to use ${cow} in recipes though!

Upvotes: 0

Joseph Quinsey
Joseph Quinsey

Reputation: 9972

This is a negative answer, unfortunately. The GNU Make Manual states (emphasis added):

The shell function performs the same function that backquotes perform in most shells: it does command expansion. This means that it takes as an argument a shell command and evaluates to the output of the command. The only processing make does on the result is to convert each newline (or carriage-return / newline pair) to a single space. If there is a trailing (carriage-return and) newline it will simply be removed.

I don't think that eval or define will help.

The only work-around that I can see is to store the cowsay hello command in a variable, instead of the command's output, as in:

OUTPUT=$$(seq 5)     # seq 5 replaces cowsay hello

all:
    @echo "$(OUTPUT)" > output.txt

This work-around is bad if cowsay hello is expensive to calculate, or has side-effects. A second work-around is to keep its output, but replace the line-breaks with any arbitrary character(s), using for example sed, and then adding the line-breaks back. For example:

OUTPUT=$(shell seq 5 | sed s/$$/xxx/)            # add xxx to the end of each line

all:
    @echo -n $(OUTPUT) ' ' | sed 's/xxx /\n/g'   # replace xxx-space with line break

Upvotes: 6

Reinier Torenbeek
Reinier Torenbeek

Reputation: 17383

I want to capture that output into a variable and then later print the variable to a file.

There does not seem to be a way around make's mechanism to translate newlines in the shell command output into spaces. A bit of a hack that stays close to your original approach would be to have the shell convert newlines into some uncommon character (like \1) when assigning the output to the variable and then have it translate it back when echo-ing that variable to the file. Something like this:

OUTPUT=$(shell cowsay hello | tr '\n' '\1')

all:
        @echo "$(OUTPUT)" | tr '\1' '\n' > output.txt

For me, this results in

$ make
$ cat output.txt 
 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Upvotes: 6

Related Questions