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?
CodePudding user response:
In foo case an external printf executable is run instead of shell builtin (see this answer https://unix.stackexchange.com/questions/202302/variable-definition-in-bash-using-the-local-keyword/202326#202326 that explains the difference between shell builtin and external executables) as you can see with strace::
$ strace -f make
(...)
[pid 1396] execve("/usr/bin/printf", ["printf", "\\x41\\n"], 0x56453618b880 /* 14 vars */ <unfinished ...>
so even though dash is used A is printed because printf which is a part of coreutils understands \x sequence:
$ dash
$ /usr/bin/printf '\x41\n'
A
In bar case make runs dash builtin as you can see with strace:
$ strace -f make bar
(...)
[pid 1414] execve("/bin/sh", ["/bin/sh", "-c", "printf '\\x41\\n' | cat"], 0x55835836a8e0 /* 14 vars */ <unfinished ...>
CodePudding user response:
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.