什么是子 shell(在 make 文档的上下文中)?

什么是子 shell(在 make 文档的上下文中)?

我正在读一本关于该make命令的书,其中有一段:

如果任何先决条件具有关联规则,请首先尝试更新这些规则。接下来,考虑目标文件。如果任何先决条件比目标新,则通过执行命令重新创建目标。每个命令行都会传递到 shell 并在其自己的子 shell 中执行。

您能解释一下那里使用的 subshel​​l 的概念以及为什么需要使用这样的 subshel​​l 吗?

答案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 功能。

相关内容