为什么从 Makefile 调用时 printf 的行为不同?
Why does printf behave differently when called from a Makefile?
printf
程序可用于打印二进制数据,例如:
$ printf '%b' '\xff\xff'
��
如果我把它单独放在一个 Makefile 中,它的工作原理是一样的:
all:
printf '%b' '\xff\xff'
$ make
printf '%b' '\xff\xff'
��
但是,如果我想在 Makefile 中的同一个 shell 调用上做任何其他事情,例如将它重定向到一个文件,或者只是在之后打印其他东西,那么尽管 Make 打印的命令没有改变(表明这不是转义问题),但输出变为反斜杠后跟“x”后跟双“f”,两次:
all:
printf '%b' '\xff\xff'; printf 'wtf?\n'
make
printf '%b' '\xff\xff'; printf 'wtf?\n'
\xff\xffwtf?
这是怎么回事?为什么一行中的两个 printf
与单个 printf
的行为不同?
@chepner 在他们的评论中走在了正确的轨道上,但细节不太正确:
This is wild speculation, but I suspect there is some sort of
optimization being applied by make
that causes the first example, as a
simple command, to be executing a third option, the actual binary
printf
(found in /usr/bin
, perhaps), rather than a shell. In your
second example, the ;
forces make
to use a shell to execute the shell
command line.
Make 始终使用 /bin/sh
作为其 shell,无论用户使用什么作为其 shell。在某些系统上,/bin/sh
是 bash(它有一个内置的 printf
),而在某些系统上 /bin/sh
是不同的东西(通常是 dash
,它是一个轻量级的, POSIX-符合 shell) 可能没有内置 shell。
在您的系统上,/bin/sh
是 bash。但是,当你有一个不需要 shell 的“简单命令”时(也就是说,make 本身有足够的简单引用智能来理解你的命令)那么为了更有效,make 将直接调用该命令而不是运行宁 shell.
这就是这里发生的事情:当您 运行 简单命令(没有 ;
)时,make 将直接调用该命令并且 运行 /usr/bin/printf
。当您 运行 更复杂的命令(包括 ;
)时,make 将直接放弃 运行ning 命令并调用您的 shell... 即 bash,它使用 bash 的内置 printf
.
基本上,您的脚本不符合 POSIX 标准(POSIX 标准中没有 %b
),因此它的作用没有明确定义。如果您希望始终使用 SAME 行为,则应使用 /usr/bin/printf
强制始终使用该行为。强制 make 总是 运行 a shell 并且从不使用它的快速路径是非常棘手的;您需要在每个命令中包含一个特殊字符,例如尾随 ;
。
printf
程序可用于打印二进制数据,例如:
$ printf '%b' '\xff\xff'
��
如果我把它单独放在一个 Makefile 中,它的工作原理是一样的:
all:
printf '%b' '\xff\xff'
$ make
printf '%b' '\xff\xff'
��
但是,如果我想在 Makefile 中的同一个 shell 调用上做任何其他事情,例如将它重定向到一个文件,或者只是在之后打印其他东西,那么尽管 Make 打印的命令没有改变(表明这不是转义问题),但输出变为反斜杠后跟“x”后跟双“f”,两次:
all:
printf '%b' '\xff\xff'; printf 'wtf?\n'
make
printf '%b' '\xff\xff'; printf 'wtf?\n'
\xff\xffwtf?
这是怎么回事?为什么一行中的两个 printf
与单个 printf
的行为不同?
@chepner 在他们的评论中走在了正确的轨道上,但细节不太正确:
This is wild speculation, but I suspect there is some sort of optimization being applied by
make
that causes the first example, as a simple command, to be executing a third option, the actual binaryprintf
(found in/usr/bin
, perhaps), rather than a shell. In your second example, the;
forcesmake
to use a shell to execute the shell command line.
Make 始终使用 /bin/sh
作为其 shell,无论用户使用什么作为其 shell。在某些系统上,/bin/sh
是 bash(它有一个内置的 printf
),而在某些系统上 /bin/sh
是不同的东西(通常是 dash
,它是一个轻量级的, POSIX-符合 shell) 可能没有内置 shell。
在您的系统上,/bin/sh
是 bash。但是,当你有一个不需要 shell 的“简单命令”时(也就是说,make 本身有足够的简单引用智能来理解你的命令)那么为了更有效,make 将直接调用该命令而不是运行宁 shell.
这就是这里发生的事情:当您 运行 简单命令(没有 ;
)时,make 将直接调用该命令并且 运行 /usr/bin/printf
。当您 运行 更复杂的命令(包括 ;
)时,make 将直接放弃 运行ning 命令并调用您的 shell... 即 bash,它使用 bash 的内置 printf
.
基本上,您的脚本不符合 POSIX 标准(POSIX 标准中没有 %b
),因此它的作用没有明确定义。如果您希望始终使用 SAME 行为,则应使用 /usr/bin/printf
强制始终使用该行为。强制 make 总是 运行 a shell 并且从不使用它的快速路径是非常棘手的;您需要在每个命令中包含一个特殊字符,例如尾随 ;
。