在 Makefile 中,我想打印一个以十六进制数表示的字节并将其传递给另一个程序的 STDIN。由于某种原因,它不起作用:
without-pipe:
@printf '\x66\x6f\x6f'
with-bash-and-pipe:
@/bin/bash -c "printf '\x66\x6f\x6f' | cat"
with-pipe:
@printf '\x66\x6f\x6f' | cat
运行此文件会产生:
$ make without-pipe
foo
$ make with-bash-and-pipe
foo
$ make with-pipe
\x66\x6f\x6f
我遗漏了什么功能make
,以及使最后一个目标产生相同输出的正确方法是什么。一个with-bash-and-pipe
是某种解决方法。
答案1
注意:我的测试平台是 Ubuntu 18.04.2 LTS。
只需几个步骤即可理解该行为。
1. Make 有点智能
它似乎make
确定是否需要 shell。我做到了
strace -f -e execve make without-pipe
输出部分为:
[pid 17526] execve("/usr/local/sbin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/local/bin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/sbin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/bin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = 0
因此在这种情况下只需探测(根据)make
的可能位置直到找到工具。printf
$PATH
最后一种情况下的行为有所不同。此命令
strace -f -e execve make with-pipe
收益率(以及其他线路)
[pid 17592] execve("/bin/sh", ["/bin/sh", "-c", "printf '\\x66\\x6f\\x6f' | cat"], 0x561465476440 /* 67 vars */) = 0
该工具非常智能,可以告诉您想要运行管道。如果您想要运行管道,则将使用 shell。shell 是sh
。
2. 三种情况采用不同的printf
实现方式
如上所示,
make without-pipe
使用printf
操作系统中可用的可执行文件。printf
是 bash 中的内置命令(用 确认type -a printf
),因此make with-bash-and-pipe
使用 bash 中的内置命令。printf
也是 sh 中的内置命令(用 确认(unset PATH; printf 'printf works\n'; ls)
;如果它不是内置命令,sh 就无法找到可执行文件,就像它找不到 一样ls
),因此make with-pipe
使用 sh 中的内置命令。
3.printf
不需要理解\x66
等
这格式操作数应使用 XBD 中描述的格式字符串文件格式符号但以下情况除外:
[…]
两个链接文档都没有说\xHH
这些必须是特殊的。我不确定规范是否明确允许它们是特殊的,但事实是它们对于printf
问题中的某些 -s 来说是特殊的。
但不适用于printf
sh 中的内置命令。
使最后一个目标产生相同输出的正确方法是什么?一种方法
with-bash-and-pipe
是采用某种解决方法。
您可以通过指定其完整路径来确保printf
您运行的不是内置命令:
/usr/bin/printf '\x66\x6f\x6f' | cat
然而这需要你事先知道路径。要解决这个问题,你可以使用env
:
env printf '\x66\x6f\x6f' | cat
env
将运行在 中找到的可执行文件$PATH
。您可以期待env
它的存在,因为它是 POSIX 所要求的。
另一方面,一般来说,您无法确定任何printf
可执行文件(无论是否内置)是否支持\xHH
。因此,真正的解决方法是重写格式字符串,以便所有符合 POSIX 标准的实现都能正确理解它printf
。这似乎很有用:
\\
除了 XBD 文件格式符号 ( ,\a
,\b
,\f
,\n
,\r
,\t
,\v
)中显示的转义序列之外,\ddd
其中ddd是一位、两位或三位八进制数,应写为一个字节,其数值由八进制数指定。
例子:
printf '\146\157\157'
这种方法在这三种情况下都有效。