英文:
The result of makefile execution is different from shell execution
问题
Here's the translation of the code part you provided:
我的 Makefile:
```make
foo:
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
当我运行 make foo
时,输出结果与直接在 Shell 上运行相同命令的结果不同(mac m2 arm64)。
MacBook-Air:tmp $ make foo
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
b6977416b3597483a9e416f4c04a1dcd
MacBook-Air:tmp$ echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
44ac6a36a27b1e5352a14e803929516f
但在 amd64 Ubuntu 环境中执行相同操作时,结果是一致的。
tmp$ make foo
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
MD5(stdin)= 44ac6a36a27b1e5352a14e803929516f
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
MD5(stdin)= 44ac6a36a27b1e5352a14e803929516f
感谢您的指导。
Please note that the translation above may contain HTML-encoded characters like `"` and `<`, which are used for displaying code in the HTML format.
<details>
<summary>英文:</summary>
my Makefile:
foo:
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
when I run: `make foo`, the output is different from the result of running the same command directly on the shell.(mac m2 arm64)
MacBook-Air:tmp $ make foo
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
b6977416b3597483a9e416f4c04a1dcd
MacBook-Air:tmp$ echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
44ac6a36a27b1e5352a14e803929516f
But when I execute the same operation in an amd64 Ubuntu environment, the results are consistent.
tmp$ make foo
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
MD5(stdin)= 44ac6a36a27b1e5352a14e803929516f
echo -n "d41d8cd98f00b204e9800998ecf8427e" | openssl dgst -md5 -hmac "1@123456"
MD5(stdin)= 44ac6a36a27b1e5352a14e803929516f
I am grateful for your guidance.
[1]: https://i.stack.imgur.com/ITmUO.png
</details>
# 答案1
**得分**: 3
以下是您要翻译的内容:
"echo"命令是内置于Shell中的,因此其行为取决于所使用的Shell,这在不同平台之间是不同的。而且,默认情况下,"make"运行的是"/bin/sh",而不是环境中设置的"$SHELL"。
如果移除"openssl"命令,您可以清晰地看到行为差异 - macOS上的"sh" echo不理解"-n"作为选项,而是将其包含在输出中。您可以这样复现这个行为:
$ /bin/sh -c 'echo -n d41d8cd98f00b204e9800998ecf8427e'
-n d41d8cd98f00b204e9800998ecf8427e
(一个复杂因素是,"make"试图智能判断何时需要Shell以及何时可以直接执行命令 - 因此,没有管道的普通"echo"可能会执行后者,并最终运行"/bin/echo",即使在Mac上也能够理解"-n"。但是,任何命令的复杂性都将导致它由Shell运行。)
您可以在"Makefile"内部设置"SHELL",但这会引入自己的可移植性问题;如果将其设置为"/bin/zsh",例如,那么"Makefile"将无法在没有安装"zsh"或将其安装在"/usr/bin"或"/usr/local/bin"中的系统上运行。
我建议将"echo -n"更改为"printf";这样,它在不同的Shell中保持一致,并且"printf"已经不会附加换行符。事实上,最好的做法可能是忘记"echo"的存在,始终改用"printf"(只有在需要换行符时才使用"\n")。
但如果您宁愿设置"SHELL",可以使用以下方式:
SHELL := $(shell echo "$$SHELL")
这告诉"make"使用环境变量设置的值,通常会是调用Shell的值。
<details>
<summary>英文:</summary>
The `echo` command is built into the shell, so its behavior depends upon that shell, which is different between platforms. And `make` by default runs `/bin/sh` rather than whatever `$SHELL` is set to in the environment.
If you remove the `openssl` command you can see the behavior difference clearly - the `sh` echo on macOS doesn't understand `-n` as an option, but just includes it in the output¹. You can reproduce the behavior like so:
$ /bin/sh -c 'echo -n d41d8cd98f00b204e9800998ecf8427e'
-n d41d8cd98f00b204e9800998ecf8427e
(A complicating factor is that `make` tries to be smart about when it needs the shell and when it can get away with just `exec()`ing the command directly - so a plain `echo` with no pipe may do the latter and wind up running `/bin/echo`, which even on a Mac understands `-n` just fine. But any complications to the command will result in it being run by the shell.)
You could set `SHELL` inside the `Makefile`, but that introduces its own set of portability problems; if you set it to `/bin/zsh`, for instance, the Makefile won't run on systems that don't have `zsh` installed, or have it in installed in `/usr/bin` or `/usr/local/bin`.
I would change the `echo -n` to `printf`; that way it's consistent across shells, and `printf` already doesn't append a newline. In fact, it's probably a good idea to just forget `echo` exists and always use `printf` instead (just with a `\n` on the end when you _do_ want the newline).
But if you'd rather set `SHELL`, I would do so with something like this:
SHELL := $(shell echo "$$SHELL")
That tells `make` to use whatever the environment variable is set to, which will normally be what the calling shell is.
¹ <sup><sub>The Bash 3.2 that ships with macOS changes its `echo` behavior when invoked as `sh`. Newer versions have stopped doing that, even with POSIXLY_CORRECT set, because while the POSIX standard specifies that `echo` shouldn't recognize `-n`, it also straight up admits `echo` is non-portable and says that it should be avoided except in unambiguous cases – i.e. you want to echo things literally and add a newline and there are no arguments that look like options or backslash escapes.</sub></sup>
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论