PID da função background, F, em comandos invocados em subshells dentro de F

0

Quando foo é executado em segundo plano, o BASHPID de foo ( bashpid_of_foo ) não está disponível nos corpos bar_1 a bar_n via $BASHPID , pois eles são invocados por meio do Recurso de Substituição de Comando de bash :

function foo() {
    local bashpid_of_foo=$BASHPID
    local output

    # desired to be shared by all Command Substitutions
    # in the body of this function.
    local log=/path/to/log.$BASHPID

    ... >> $log

    output=$(bar_1 ...)
      ...
    output=$(bar_n ...)
}

function bar_1() {
   # log only specific (and NOT all) messages
   # to the shared log file of the invoking thread.
    ... >> /path/to/log.$BASHPID
}

foo &
foo &

Pergunta: Existe uma maneira elegante de contornar a limitação acima, sem ter que passar bashpid_of_foo via variáveis de ambiente adhoc ou arquivos de disco externos?

Por elegante , quero dizer, poder manter as interfaces e corpos de bar_* funções limpas confiando apenas nos recursos fornecidos pelo bash. (Por exemplo, BASHPID é um recurso bash .)

Se eu tentar substituir o valor de BASHPID , assim,

out_1=$(BASHPID=$BASHPID bar_1 ...)

... (corretamente) reclama que BASHPID é uma variável somente leitura.

EDIT: (1) Adicionado a definição de bar_1 acima. (2) Adicionado a segunda chamada para foo em segundo plano. Cada invocação de foo precisa manter seu próprio arquivo de log, já que gravar em um arquivo comum pode resultar em conteúdo ilegível.

OBSERVAÇÃO: Qualquer que seja o registro em log no contexto de tempo de execução de foo , quero que ele entre no arquivo de log foo -específico, /path/to/log.$BASHPID SEM passando o nome deste arquivo de log ou até mesmo o PID de foo . foo pode ter várias instâncias em execução em segundo plano.

    
por Harry 27.07.2018 / 08:57

2 respostas

1
#!/bin/bash

bar () {
    # bashpid is set in our environment from the calling function
    printf 'bar BASHPID = %d, bar bashpid = %d\n' "$BASHPID" "$bashpid"

    # in your case, you would have...

    local logfile="/some/path/to/log.$bashpid"

    # etc.
}

foo () {
    local bashpid="$BASHPID"
    local message

    local logfile="/some/path/to/log.$BASHPID"

    message=$( bashpid="$bashpid" bar ); printf 'Message from bar: %s\n' "$message"
    message=$( bashpid="$bashpid" bar ); printf 'Message from bar: %s\n' "$message"
    message=$( bashpid="$bashpid" bar ); printf 'Message from bar: %s\n' "$message"
    message=$( bashpid="$bashpid" bar ); printf 'Message from bar: %s\n' "$message"
}

foo &
foo &
foo &

wait

Exemplo de execução:

$ bash script.sh
Message from bar: bar BASHPID = 71979, bar bashpid = 18461
Message from bar: bar BASHPID = 7420, bar bashpid = 71036
Message from bar: bar BASHPID = 6109, bar bashpid = 18461
Message from bar: bar BASHPID = 27868, bar bashpid = 71036
Message from bar: bar BASHPID = 44547, bar bashpid = 60086
Message from bar: bar BASHPID = 69310, bar bashpid = 71036
Message from bar: bar BASHPID = 37649, bar bashpid = 60086
Message from bar: bar BASHPID = 15999, bar bashpid = 71036
Message from bar: bar BASHPID = 81520, bar bashpid = 18461
Message from bar: bar BASHPID = 92568, bar bashpid = 60086
Message from bar: bar BASHPID = 72438, bar bashpid = 18461
Message from bar: bar BASHPID = 15094, bar bashpid = 60086

Para cada chamada de foo na parte principal do script, quatro chamadas para bar serão feitas e quatro linhas de saída serão produzidas. Como você pode ver, há apenas três números bashpid exclusivos, cada um de uma das invocações foo .

Outra maneira de passar $bashpid de foo para bar é obviamente passá-lo como um argumento de linha de comando e recebê-lo com local bashpid="$1" em bar ou algo semelhante, mas você diz que não Não quero fazer isso.

    
por 27.07.2018 / 11:28
1

OBSERVAÇÃO: Em relação às suas perguntas gerais em torno de $BASHPID , isso é somente leitura e você não pode manipulá-lo. Isso ocorre por design.

Normalmente é meu conselho geral que quando você chegar ao nível de sofisticação com scripts Bash como este, é hora de movê-lo para Python, Ruby, o que quer que seja.

Seu exemplo

Acho que não vejo qual é o seu problema, isso funciona para mim:

$ cat subby.bash
#!/bin/bash

function foo() {
    local bashpid_of_foo=$BASHPID
    local output=blipblop

    echo "foo: $bashpid_of_foo"
    echo "foo: $output"
    out_1=$(echo $bashpid_of_foo)
    out_n=$(echo $output)
    echo "out_1: $out_1"
    echo "out_n: $out_n"
}

foo &


$ ./subby.bash
foo: 4900
foo: blipblop
out_1: 4900
out_n: blipblop

Adição de exportação

Se mudarmos isso, transformando bar_1 em um script de shell:

$ cat bar_1
#!/bin/bash

echo "from bar_1: $bashpid_of_foo"

E altere seu script original assim:

#!/bin/bash

function foo() {
    export bashpid_of_foo=$BASHPID
    local output=blipblop

    echo "foo: $bashpid_of_foo"
    echo "foo: $output"
    out_1=$(echo $bashpid_of_foo)
    out_2=$(./bar_1)
    out_n=$(echo $output)
    echo "out_1: $out_1"
    echo "out_2: $out_2"
    echo "out_n: $out_n"
}

foo &

Podemos ver que o $bashpid_of_foo está sendo exportado corretamente para subshells:

$ ./subby.bash
foo: 5014
foo: blipblop
out_1: 5014
out_2: from bar_1: 5014
out_n: blipblop

Precisamos usar um export aqui e não apenas um local , caso contrário as variáveis de ambiente não serão exportadas para nenhum filho. As subshells são cascas de crianças aqui.

$ help export
...
   Marks each NAME for automatic export to the environment of subsequently
    executed commands.  If VALUE is supplied, assign VALUE before exporting.
    
por 27.07.2018 / 09:31