【问题标题】:Why does printf behave differently when called from a Makefile?为什么从 Makefile 调用时 printf 的行为会有所不同?
【发布时间】:2021-09-14 14:01:20
【问题描述】:

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?

这里发生了什么?为什么一行中的两个 printfs 与单个 printf 的行为不同?

【问题讨论】:

  • printf 是许多 shell 中内置的;您可能正在使用bash 作为您的交互式shell,但make 正在使用您的系统默认shell,它可能不是bash。因此,有两个不同的printf 命令。
  • @chepner 但这如何解释两个Makefiles 之间的不同printf 行为?
  • 这是一个疯狂的猜测,但我怀疑make 应用了某种优化,导致第一个示例作为一个简单的命令执行第三个选项,实际的二进制@ 987654337@(可能在/usr/bin 中找到),而不是shell。在您的第二个示例中,; 强制 make 使用 shell 执行 shell 命令行。

标签: shell makefile printf escaping quoting


【解决方案1】:

@chepner 在他们的评论中走在正确的轨道上,但细节并不完全正确。

Make 总是使用/bin/sh 作为它的 shell,不管用户使用什么作为他们的 shell。在某些系统上,/bin/sh 是 bash(它具有内置的 printf),而在某些系统上,/bin/sh 是不同的(通常是 dash,它是一个轻量级、符合 POSIX 的外壳),它可能没有内置外壳。

在您的系统上,/bin/sh 是 bash。但是,当你有一个不需要 shell 的“简单命令”时(也就是说,make 本身有足够的简单的引用智能来理解你的命令),那么为了更有效,make 将直接调用该命令而不是运行 shell。

这就是这里发生的事情:当您运行简单命令(没有;)时,make 将直接调用该命令并运行/usr/bin/printf。当您运行更复杂的命令(包括;)时,make 将放弃直接运行该命令并调用您的shell……这是bash,它使用bash 的内置printf

基本上,您的脚本不符合 POSIX 标准(POSIX 标准中没有 %b),因此它的功能没有明确定义。如果您希望始终使用相同的行为,则应使用 /usr/bin/printf 强制始终使用该行为。强制 make 总是运行一个 shell 并且从不使用它的快速路径要棘手得多;您需要在每个命令中包含一个特殊字符,例如尾随 ;

【讨论】:

  • 我认为优化是特定于实现的(可能是 GNU make 这里)。我在 POSIX 规范中没有看到任何提及以除 shell 以外的任何方式执行命令行的内容(但话又说回来,POSIX 规范很长,我当然还没有完整地阅读整个内容)。跨度>
  • 有没有办法在不指定完整路径的情况下运行程序printf 而不是内置的shell?
  • 确实优化是 GNU make 所做的,它不是 POSIX 的一部分。理论是,如果您有一个符合要求的系统和符合要求的 makefile,那么应该不可能区分使用快速路径和慢速路径。如果您的系统和/或 makefile 不符合标准,那么所有的赌注都将关闭:)。
  • 见我上面的最后一段。您可以在每个 printf 的末尾添加一个分号,以强制它不使用快速路径。
  • @MadScientist 我的问题是是否有一种方法可以使用printf 程序(即我可以通过显式编写/usr/bin/printf 得到什么),而无需在Makefile 中硬编码/usr/bin 路径。所以目前在我真正的 Makefile 中,我有 (/usr/bin/printf '%b' '\xff\xff'; cat $<) > $@ 作为 make 规则。如何在不硬编码 /usr/bin 的情况下重写它,但保留外部(非 shell 内置)printf
猜你喜欢
  • 1970-01-01
  • 2010-10-18
  • 2012-08-28
  • 2020-08-06
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多