O que é um subshell (no contexto da documentação do make)?

5

Estou lendo um livro sobre o comando make e há um parágrafo:

If any of the prerequisites has an associated rule, make attempts to update those first. Next, the target file is considered. If any prerequisite is newer than the target, the target is remade by executing the commands. Each command line is passed to the shell and is executed in its own subshell.

Você poderia explicar o conceito de subshell usado lá e por que é necessário usar tal subshell?

    
por xralf 31.10.2011 / 20:50

4 respostas

8

make(1) em si não sabe como executar os comandos do shell. Poderia ter sido feito para isso, mas o Unix Way é ter preocupações bem separadas: make(1) sabe como construir gráficos de dependência que determinam o que deve ser feito, e sh(1) sabe como executar comandos.

O ponto que o autor está tentando fazer é que você não deve escrever essas linhas de comando, de modo que uma mais tarde dependa de uma anterior, exceto através do sistema de arquivos. Por exemplo, isso não funcionará:

sometarget: some.x list.y of-dependencies.z
    foo='run-some-command-here'
    run-some-other-command $$foo

Se este fosse um script de shell de duas linhas, a saída do primeiro comando seria passada como um argumento para o segundo comando. Mas como cada um desses comandos é executado em um sub-shell separado, o valor da variável $foo é perdido após o primeiro sub-shell existir, portanto, não há nada para passar para o primeiro.

Uma maneira de contornar isso, como sugerido acima, é usar o sistema de arquivos:

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)'

Isso armazena a saída do primeiro comando em um local persistente para que o segundo comando possa carregar o valor.

Outra coisa que tropeça make(1) newbies algumas vezes é que construções que geralmente são divididas em várias linhas para legibilidade em um script de shell devem ser escritas em uma única linha ou agrupadas em um script de shell externo quando você as executa em Makefile . Loops são um bom exemplo; isso não funciona:

someutilitytarget:
    for f in *.c
    do
        munch-on $f
    done

Você precisa usar ponto e vírgula para obter tudo em uma única linha:

someutilitytarget:
    for f in *.c ; do munch-on $f ; done

Para mim, sempre que isso me fornece uma linha de comando com mais de 80 caracteres, movo-a para um script de shell externo para que seja legível.

    
por 31.10.2011 / 21:04
3

Make não é um shell, então ele precisa executá-los em um shell. O livro está chamando-os de "subshells" porque make é em si (muito provavelmente) executado em um shell. O que o livro está realmente tentando transmitir é que cada comando recebe seu próprio shell, portanto, se, por exemplo, Se você deseja exportar uma variável em uma linha e usá-la na próxima linha (ou em outra subseqüente), ela não funcionará (o export termina com o shell em que é usado e a próxima linha é executada em um shell separado ).

    
por 31.10.2011 / 21:03
1

O que significa que toda linha de comando dentro de uma regra será chamada em seu próprio processo de shell. A razão é que o make executa uma linha de comando e depois quer verificá-lo antes de chamar a próxima linha de comando.

Poder-se-ia pensar em outras maneiras de chamar os comandos, mas é assim que make foi criado.

É claro que make poderia apenas fork () / exec () os comandos, mas usar um subprocesso shell também permite que o usuário use recursos do shell.

    
por 31.10.2011 / 21:01
0

Pergunta antiga, mas você também pode executar o comando no shell e obter a saída

link

OUTPUT = $(shell pwd)

some-target:
    @echo $(OUTPUT)
    
por 20.08.2018 / 01:29