Home > OS >  Output of printf '\x41\n' | cat in Makefile is different than the output in shell
Output of printf '\x41\n' | cat in Makefile is different than the output in shell

Time:11-14

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.

  • Related