我正在Linux上使用GNU Make。这是我的Makefile。
foo:
printf '\x41\n'
bar:
printf '\x41\n' | cat
输出如下:
$ make foo
printf '\x41\n'
A
$ make bar
printf '\x41\n' | cat
\x41
使用的shell是Dash:
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Sep 12 04:41 /bin/sh -> dash
为什么当我通过cat
进行管道传输时,输出会变得不同?
奇怪的是,当我直接在dash
上执行命令时,它本身的行为会有所不同。
$ printf '\x41\n'
\x41
$ printf '\x41\n' | cat
\x41
这是怎么回事?为什么Makefile中的命令输出与Dash中的不一致?
3条答案
按热度按时间xoshrz7s1#
在 foo 情况下,运行的是外部printf可执行文件,而不是shell builtin(请参阅此答案https://unix.stackexchange.com/questions/202302/variable-definition-in-bash-using-the-local-keyword/202326#202326,它解释了shell builtin和外部可执行文件之间的区别),正如您在strace:中看到的那样:
因此,即使使用了破折号,也会打印A,因为printf是coreutils的一部分,它理解\x序列:
在 bar 情况下,如您在strace中所见,make runs dash是内置的:
eiee3dmh2#
您看到不同行为的原因是形式
\xNN
没有在POSIX标准中定义printf
。该标准仅使用\oNNN
定义八进制中指定的特殊字符的行为。它不支持十六进制形式。看到不同行为的原因在于,在简单的情况下:
make
不需要调用shell:它可以直接运行命令,调用系统上的/usr/bin/printf
程序。除了POSIX所要求的功能外,该程序还有额外的功能,它可以处理\xNN
字符。在更复杂的情况下:
这需要
make
来调用shell,因为make
不能处理管道等。当您调用shell时,它使用的是shell的内置printf
,而不是单独的程序/usr/bin/printf
。(在大多数情况下),它的printf
内置函数不能处理非标准的\xNN
字符。简短回答:坚持使用POSIX定义的行为,它将一直工作。
yh2wf1be3#
让我们使用一行语句总结其他答案的结果,其中的输出提供了所有必要的证据,以了解:
cat
时得到不同结果的问题make
在printf
未通过管道传输的情况下运行系统printf
,并且带有printf
的shell通过管道传输到cat
。除了printf
的POSIX标准仅使用\NNN
定义了八进制表示法中指定的特殊字符的行为(但不支持\xNN
)之外,您看到不同行为的核心原因是什么?在提供的命令行的帮助下,任何人现在都可以尝试在自己的系统上重现该问题,副作用是查看make使用了哪个shell:
$ cat生成文件;回声-----------; stace-o stace.过时;回声-----------;猫爬出|grep '打印'| grep -v '枚举'
(don“别忘了把输出的第一部分存储在一个名为
"Makefile"
的文件之前):顺便说一下:如果你看一下
"strace.out"
文件的第一行,你可以看到哪个make正在处理这个Makefile:下面的代码
"Makefile"
保存了运行前的长命令行:如果您的
make
没有给予与上面相同的结果,请将其调整为make all
,以获得所有输出。P.S.请注意,
"Makefile"
中printf
行的缩进必须使用制表符,而不是空格(因此,请用制表符替换空格,或从编辑和非查看文本框中复制脚本)。P.S. P.S.正如您在
strace
的输出中所看到的,在我的系统上运行make
所调用的shell是sh
,而系统Terminal
的标准shell是bash
。因此,与在终端中运行它们相比,在make
中运行的“相同”命令在行为上可能有所不同。由于这很容易导致混淆,因此,在使用命令行运行命令或可执行文件时,通常最好充分意识到,如果由于存在多个shell或不同的命令具有相同的名称。而且,充分意识到这样一个事实也是一个好主意,即在一个给定的shell中运行完全相同的程序,如果在另一个shell中运行或没有连接到shell,则是不完全相同的事情。