为什么printf的行为不同时,从Makefile调用?



printf程序可用于打印二进制数据,例如:

$ printf '%b' 'xffxff'
��

如果我把它单独放在Makefile中,它的工作原理是一样的:

all:
printf '%b' 'xffxff'
$ make
printf '%b' 'xffxff'
��

但是,如果我想在Makefile中对相同的shell调用做任何其他事情,例如将其重定向到一个文件,或者只是在之后打印其他内容,那么尽管Make打印的命令不会改变(表明这不是转义问题),但输出更改为反斜杠后跟"x"后面跟着两个"f",两次:

all:
printf '%b' 'xffxff'; printf 'wtf?n'
make
printf '%b' 'xffxff'; printf 'wtf?n'
xffxffwtf?

这是怎么回事?为什么一行中的两个printf与单个printf的行为不同?

@chepner的评论是正确的,但细节不太正确:

这是疯狂的猜测,但我怀疑有某种make应用的优化导致了第一个例子,作为一个简单的命令,要执行第三个选项,实际的二进制printf(可能在/usr/bin中找到),而不是shell。在你的第二个例子,;强制make使用shell来执行shell命令行。

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将放弃直接运行命令并调用您的shell…这是bash,它使用bash的内置printf

基本上,您的脚本不符合POSIX (POSIX标准中没有%b),因此它所做的事情没有定义良好。如果你想要相同的行为,你应该使用/usr/bin/printf来强制它总是被使用。强迫make总是运行一个shell而不使用它的快速路径要棘手得多;您需要在每个命令中包含一个特殊字符,如末尾的;

最新更新