我正在读一本关于该make
命令的书,其中有一段:
如果任何先决条件具有关联规则,请首先尝试更新这些规则。接下来,考虑目标文件。如果任何先决条件比目标新,则通过执行命令重新创建目标。每个命令行都会传递到 shell 并在其自己的子 shell 中执行。
您能解释一下那里使用的 subshell 的概念以及为什么需要使用这样的 subshell 吗?
答案1
make(1)
本身不知道如何运行shell命令。本来可以这样做,但 Unix 方式是有很好分离的关注点:make(1)
知道如何构建依赖关系图来确定必须做什么,并且sh(1)
知道如何运行命令。
作者试图指出的一点是,除了通过文件系统之外,您不能编写那些命令行,使得后一个命令行依赖于前一个命令行。例如,这是行不通的:
sometarget: some.x list.y of-dependencies.z
foo=`run-some-command-here`
run-some-other-command $$foo
如果这是一个两行 shell 脚本,第一个命令的输出将作为参数传递给第二个命令。但由于每个命令都在单独的子 shell 中运行,因此$foo
在第一个子 shell 存在后变量的值就会丢失,因此没有任何内容可以传递给第一个子 shell。
正如上面所暗示的,解决这个问题的一种方法是使用文件系统:
TMPDIR=/tmp
TMPFILE=$(TMPDIR)/example.tmp
sometarget: some.x list.y of-dependencies.z
run-some-command-here > $(TMPFILE)
run-some-other-command `cat $(TMPFILE)`
它将第一个命令的输出存储在持久位置,以便第二个命令可以加载该值。
有时令新手困惑的另一件事make(1)
是,在 shell 脚本中为了可读性而通常分成多行的构造必须写在一行上,或者当您在Makefile
.循环就是一个很好的例子;这不起作用:
someutilitytarget:
for f in *.c
do
munch-on $f
done
您必须使用分号将所有内容放在一行上:
someutilitytarget:
for f in *.c ; do munch-on $f ; done
对于我自己来说,只要这样做导致命令行长度超过 80 个字符左右,我就会将其移至外部 shell 脚本中,以便可读。
答案2
Make 本身不是 shell,因此它需要在 shell 中执行它们。本书将它们称为“子 shell”,因为 make 本身(很可能)在 shell 中运行。这本书实际上试图传达的是,每个命令都有自己的 shell,因此,如果您想在一行上导出一个变量并在下一行(或另一个后续行)中使用它,它不会工作(export
以它所使用的 shell 结尾,下一行在单独的 shell 中运行)。
答案3
老问题,但您也可以在 shell 中运行命令并获取输出
http://electric-cloud.com/blog/2009/03/makefile-performance-shell/
OUTPUT = $(shell pwd)
some-target:
@echo $(OUTPUT)
答案4
这意味着规则中的每个命令行都将在其自己的 shell 进程中调用。原因是 make 执行了一个命令行,然后想要在调用下一个命令行之前检查它。
人们可以想到其他方法来调用命令,但这就是方法制作是当时设计的。
当然制作可以只是fork()/exec()命令但使用 shell 子进程也允许用户使用 shell 功能。