bash Makefile 使用Shell 创建变量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39950690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Makefile Use Shell to create variables
提问by LambdaBeta
I am using make's shell command to populate some variables, and on output it indicates that they are being set to the values I expect. However when I pass them to my make recipes they all show up empty. I suspect make is doing some odd parsing on the results. Eg:
我正在使用 make 的 shell 命令来填充一些变量,并且在输出时它表明它们被设置为我期望的值。然而,当我将它们传递给我的制作食谱时,它们都显示为空。我怀疑 make 正在对结果进行一些奇怪的解析。例如:
MyTarget: $(MySources)
LINE='$(shell cat $< | grep GIMME_THE_LINE_I_WANT)'
CH9=$(shell echo $(LINE) | cut -b9)
echo $(CH9) # doesn't print anything
I checked my generator commands manually by setting SHELL=sh -XV
and when I run identical commands I get the right values, it just looks like bash is 'zeroing' my variables. Any idea what's wrong?
我通过设置手动检查了我的生成器命令SHELL=sh -XV
,当我运行相同的命令时,我得到了正确的值,看起来 bash 正在“归零”我的变量。知道出了什么问题吗?
回答by larsks
There are several things going on here. The first is that when you have:
这里有几件事情正在发生。首先是当你有:
MyTarget: $(MySources)
LINE='$(shell cat $< | grep GIMME_THE_LINE_I_WANT)'
You are setting a shellvariable called LINE
, not a make
variable. The build instructions for a target are all shell commands. So after the first line, the shell variable $LINE
contains GIMME_THE_LINE_I_WANT
. However...
您正在设置一个名为的shell变量LINE
,而不是一个make
变量。目标的构建指令都是 shell 命令。所以在第一行之后,shell 变量$LINE
包含GIMME_THE_LINE_I_WANT
. 然而...
...each linein the build instructions for a target runs in a separate shell process. So if you have:
...目标的构建指令中的每一行都在单独的 shell 进程中运行。所以如果你有:
mytarget:
MYVAR=foo
echo $$MYVAR
You'll see no output, because $MYVAR
isn't set in the context of the second command. Also note the use of $$
here, because otherwise the $
would be interpreted by Make (that is, writing $MYVAR
would actually be the make expression $M
followed by the text YVAR
). You can resolve this by logically joining your lines into a single shell script, like this:
您将看不到任何输出,因为$MYVAR
未在第二个命令的上下文中设置。还要注意$$
这里的使用,因为否则 the$
将被 Make 解释(也就是说,写作$MYVAR
实际上是 make 表达式$M
后跟 text YVAR
)。您可以通过将您的行逻辑连接到单个 shell 脚本来解决此问题,如下所示:
mytarget:
MYVAR=foo; \
echo $$MYVAR
The \
is Makefile syntax that extends a single logical line over multiple physical lines, and of course ;
is simply shell syntax for combining multiple commands on one line.
的\
是延伸在多个物理线路的单个逻辑线路生成文件的语法,当然;
是在一行的多个命令组合简单地壳语法。
With all this in mind, we could rewrite your target like this:
考虑到所有这些,我们可以像这样重写你的目标:
MyTarget: $(MySources)
LINE=$$(cat $< | grep GIMME_THE_LINE_I_WANT); \
CH9=$$(echo $$LINE | cut -b9); \
echo $$CH9
Notice that since we are alreadyrunning a shell script I'm not using Make's $(shell ...)
construct, and that I'm making sure to escape all of the $
characters to ensure that the shell, not Make, is handling variable expansion.
请注意,因为我们已经在运行一个 shell 脚本,所以我没有使用 Make 的$(shell ...)
构造,并且我确保转义所有$
字符以确保 shell 而不是 Make 正在处理变量扩展。
Taking it just a little further, you don't need to use cat
in that script; you could simply write:
再深入一点,您不需要cat
在该脚本中使用;你可以简单地写:
MyTarget: $(MySources)
LINE=$$(grep GIMME_THE_LINE_I_WANT $<); \
CH9=$$(echo $$LINE | cut -b9); \
echo $$CH9
Or:
或者:
MyTarget: $(MySources)
CH9=$$(grep GIMME_THE_LINE_I_WANT $< | cut -b9); \
echo $$CH9
(NB: While not germane to this solution, it's although worth noting that each invocation of $(shell ...)
is also run in a separate process, so a variable set in one won't be available in another.)
(注意:虽然与此解决方案没有密切关系,但值得注意的是,每次调用$(shell ...)
也运行在单独的进程中,因此在一个进程中设置的变量在另一个进程中将不可用。)
回答by blackpen
The make runs every command in its separate shell. So, the values are not carried over.
make 在其单独的 shell 中运行每个命令。因此,这些值不会结转。
When in doubt, you could always debug it with -d
option. Also, a site note, the debug option is very useful when you are trying to figure out why a rule did not fire the way you had intended it.
如有疑问,您始终可以使用-d
选项对其进行调试。此外,站点注释,当您试图找出规则未按预期方式触发的原因时,调试选项非常有用。
~> cat Makefile
MyTarget:
LINE="somevalue"
echo ${LINE}
~>
~>
~> make -d MyTarget | tail -10
LINE="somevalue"
Putting child 0x2004fbb0 (MyTarget) PID 4052 on the chain.
Live child 0x2004fbb0 (MyTarget) PID 4052
Reaping winning child 0x2004fbb0 PID 4052
echo
Live child 0x2004fbb0 (MyTarget) PID 4192
Reaping winning child 0x2004fbb0 PID 4192
Removing child 0x2004fbb0 PID 4192 from chain.
Successfully remade target file 'MyTarget'.
~>
Just clarifying for the people who downvoted and commented that my above solution doesn't work: First, I apologize for my laziness. May be I should have been clear. Let me try again.
只是向那些反对和评论我的上述解决方案不起作用的人澄清一下:首先,我为我的懒惰道歉。可能我应该已经清楚了。让我再试一遍。
The "make -d" is not a solution to OP's problem.
“make -d”不是 OP 问题的解决方案。
I tried to show OP how he/she could use debug option to solve a variety of problems that people come across while using makefiles (which, I admit, goes in a slight tangent than just solving the OP's problem at hand).
我试图向 OP 展示他/她如何使用调试选项来解决人们在使用 makefile 时遇到的各种问题(我承认,这与仅解决 OP 手头的问题略有不同)。
The above debug shows that the first command was executed in a shell with PID=4052 and the second command was executed in another shell with PID=4192 (which doesn't carry the value of that variable). Also it shows that using a variable with single dollar (${LINE}) just gives you a blank (because the makefile doesn't interpret it as a shell variable).
上面的调试显示第一个命令是在一个 PID=4052 的 shell 中执行的,第二个命令是在另一个 PID=4192 的 shell 中执行的(它不携带该变量的值)。它还表明,使用带有一美元的变量 (${LINE}) 只会给您一个空白(因为 makefile 不会将其解释为 shell 变量)。
Again, to be clear: "make -d" is not a solution. Just combine the commands in one line, separated by commas, use double dollars; if the line is long, escape the new lines.
再次明确:“make -d”不是解决方案。只需将命令组合在一行中,以逗号分隔,使用双美元;如果该行很长,请转义新行。
MyTarget:
LINE="somevalue"; \
echo $${LINE}