为什么 echo -e 在 Makefile 中表现得很奇怪?

为什么 echo -e 在 Makefile 中表现得很奇怪?

我正在编写一个 Makefile(在 Ubuntu 20.04 上,如果相关的话),并注意到echo.以这个简单的 Makefile 为例:

test.txt:
        @echo -e 'hello\nworld'
        @echo -e 'hello\nworld' > test.txt

当我运行时make,我希望在 stdout 上看到与 test.txt 中相同的内容,但事实上我没有。我在标准输出上得到这个:

hello
world

但这在 test.txt 中:

-e hello
world

同时,如果我-e从 Makefile 中的两行中删除,我会在标准输出上得到:

hello\nworld

在 test.txt 中:

hello
world

这让我想知道是否echo检测到重定向并表现出不同的行为,但当我在 shell 中手动运行它时却没有/bin/echo -e 'hello\nworld' > test.txt(如我通常期望的那样,它会在单独的行上产生hello和)。world我什至通过添加一行来确认 Makefile 使用的是 /bin/echo 而不是内置的 shell @echo --version

这里发生了什么?

答案1

需要UNIX 兼容的实现echo才能-e<space>hello<newline>world<newline>在那里输出。

那些不符合规定的人不符合要求。许多都不是,这意味着它几乎不可能便携式使用echoprintf应该使用bash's echo,在它的一些(大多数)版本中,仅当您同时启用posixxpg_echo选项时才符合要求。这可能是echo您所期望的行为。

echoGNU 附带的独立实用程序也是如此coreutils,只有在其环境中使用$POSIXLY_CORRECTset 调用它时才兼容(并且是足够新的版本)。

make通常运行sh以解释每个操作行上的命令行。

make然而,作为一种优化,如果代码足够简单并且它认为不需要调用 shell 来解释它,则 的GNU 实现可以直接运行命令。

这就解释了为什么echo --version给你/bin/echo, 但echo ... > file需要一个 shell 来执行重定向。

您可以使用strace -fe execve make来查看make执行的内容(如果不是 Linux,则在您的系统上使用truss/ ... 等效项)。tusc

在这里,虽然您的/bin/echo不兼容,但您的sh内置echo是兼容的。

printf如果您想扩展echo- 样式转义序列,请在此处使用:

printf '%b\n' 'hello\nworld'

在其格式参数,理解 C 风格的转义序列(与 (echo) 与(C) 八进制序列printf的回声风格的转义序列有区别)\0xxx\xxx

printf 'hello\nworld\n'

在这里,你还可以这样做:

printf '%s\n' hello world

这是在单独的行上输出多个参数的常见且可移植的方法。

另一种方法是添加:

SHELL = bash

到您的Makefileformake来调用bash(假设它已安装并在 中找到$PATH)而不是sh解释命令行。或者调用makemake <target> SHELL=bash.

这不一定会使其更加可移植,因为bash现在有越来越多的系统默认安装,但有些系统(例如在 Solaris 上)的bash构建使其echo内置默认情况下以标准方式运行。

设置SHELL为除了之外的任何值也会产生禁用上述 GNU 优化/bin/sh的副作用,因此使用or ,您将在两次调用之间获得一致的行为,同时仍然不必添加对 bash 的依赖项。makemake <target> SHELL=shmake <target> SHELL=/bin//sh

相关内容