I am using GNU Make on Linux. Here is my Makefile.
foo:
printf '\x41\n'
bar:
printf '\x41\n' | cat
Here is the output:
$ make foo
printf '\x41\n'
A
$ make bar
printf '\x41\n' | cat
\x41
The shell being used is Dash:
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Sep 12 04:41 /bin/sh -> dash
Why does the output become different when I pipe through cat
?
Strangely, dash
itself has a different behavior when I execute the commands directly on it.
$ printf '\x41\n'
\x41
$ printf '\x41\n' | cat
\x41
What is going on here? Why is the output of the commands in Makefile inconsistent with that in Dash?
The reason you see different behavior is that the form \xNN
is not defined in the POSIX standard for printf
. The standard only defines the behavior for special characters specified in octal, using \oNNN
. It doesn't support the hex form.
The reason for seeing different behavior is that in the simple case:
foo:
printf '\x41\n'
make
doesn't need to invoke a shell: it can run the command directly which invokes the /usr/bin/printf
program on your system. That program has extra features, in addition to what POSIX requires, and it can handle \xNN
characters.
In the more complicated case:
bar:
printf '\x41\n' | cat
This requires make
to invoke the shell, because make
can't handle pipelines etc. When you invoke the shell it's using the shell's built-in printf
not the separate program /usr/bin/printf
. Since your shell is dash
, which only provides POSIX-conforming behaviors (for the most part), its printf
built-in doesn't handle non-standard \xNN
characters.
Short answer: stick with behaviors defined by POSIX and it will work all the time.