Como tratar corretamente o SIGINT para os scripts Bash interativos e não interativos?

0

Backgound

Eu li uma postagem sobre como lidar com SIGINT signal, mas ainda não entendi como para manipulá-lo adequadamente no código que será originado e usado por ambos os shells interativos e não interativos.

Vou dar um exemplo simplificado do meu roteiro e fazer perguntas para partes específicas.

Exemplo

Eu tenho um script com funções úteis que devem ser originadas e usadas em qualquer lugar.

/tmp/useful_functions.sh

#!/bin/bash

function example()
{
    echo "Main script started"

    # Make process run forever in the background.
    ( while sleep 1; do echo "Background script running"; done ) &

    # Make subshell work for 10 seconds.
    local output="$(sleep 10; echo "Subshell completed")"
    echo "${output}"

    # Kill the backgrounded process once the subshell has finished.
    { kill "${!}" && wait "${!}"; } &> /dev/null

    echo "Main script completed"
}

Teste 1

Permite usar a função exemplo em outro script.

/tmp/test.sh

#!/bin/bash

source /tmp/useful_functions.sh

example

Agora vamos executar /tmp/test.sh e pressionar Control-C enquanto a subshell está funcionando.
(subshell = $(sleep 10; echo "Subshell completed") )

Resultados de Test 1

Processos

/tmp/test.sh , example , subshell e background foram finalizados.
O prompt aguardava novas entradas.

Perguntas

  1. Em que ordem SIGINT foi enviada / manipulada / propagada entre processos?
    (Mencionado post afirma que SIGINT deve ser enviado para todos os processos em primeiro plano e que o mais interno deve primeiro tratá-lo.)
  2. Por que o processo em segundo plano foi encerrado?

Teste 2

Permite usar a função exemplo diretamente no Bash interativo:

...$ source /tmp/useful_functions.sh
...$ example

e novamente pressione Control-C enquanto a subshell está funcionando.
(subshell = $(sleep 10; echo "Subshell completed") )

Resultados de Test 2

Processos

example e subshell foram finalizados, mas o processo backgrounded permaneceu ativo.
O prompt aguardava novas entradas, embora a saída do processo backgrounded tenha sido impressa.

Perguntas

  1. Em qual ordem foi SIGINT enviada / manipulada / propagada entre processos?
  2. Por que o processo em segundo plano NÃO foi encerrado agora?

Exemplo melhorado

Mesmo que eu não entenda totalmente o comportamento de Test 1 , esse comportamento é desejado e eu queria realizar isso também com o Bash interativo.
A idéia era criar outra subcamada que envolvesse o processo em segundo plano e a subcamada existente e esperasse que a finalização desse novo processo levasse à finalização do processo em segundo plano e da subcamada existente.

/tmp/useful_functions.sh

#!/bin/bash

function example()
{
    (
        echo "Main script started"

        # Make process run forever in the background.
        ( while sleep 1; do echo "Background script running"; done ) &

        # Make subshell work for 10 seconds.
        local output="$(sleep 10; echo "Subshell completed")"
        echo "${output}"

        # Kill the backgrounded process once the subshell has finished.
        { kill "${!}" && wait "${!}"; } &> /dev/null

        echo "Main script completed"
    )
}

Resultados de Test 1 repetidos

Processos

/tmp/test.sh , example , outer subshell , inner subshell e background foram encerrados.
O prompt aguardava novas entradas.

Perguntas

  1. Em qual ordem foi SIGINT enviada / manipulada / propagada entre processos?
  2. Por que o processo em segundo plano foi encerrado?

Resultados de Test 2 repetidos

Processos

example , outer subshell , inner subshell e background foram finalizados.
O prompt aguardava novas entradas.

Perguntas

  1. Em qual ordem foi SIGINT enviada / manipulada / propagada entre processos?
  2. Por que o processo em segundo plano foi encerrado?

Problema

Neste ponto, percebi que, na verdade, preciso fazer algumas limpezas após o processo em segundo plano e que preciso prender SIGINT em algum lugar para lidar com isso.

Solução

Como não sei como SIGINT é tratado e propagado geralmente no Bash, fiz algumas suposições e encontrei uma solução que acho que funciona corretamente, mas não tenho certeza.

#!/bin/bash

function example()
{
    (
        echo "Main script started"

        # Make process run forever in the background.
        (
            trap "echo Cleanup; trap - SIGINT; kill -s SIGINT ${$}" SIGINT
            while sleep 1; do echo "Background script running"; done
        ) &

        # Make subshell work for 10 seconds.
        local output="$(sleep 10; echo "Subshell completed")"
        echo "${output}"

        # Kill the backgrounded process once the subshell has finished.
        { kill "${!}" && wait "${!}"; } &> /dev/null

        echo "Main script completed"
    )
}

Perguntas

  1. Minha armadilha manipula / propaga adequadamente SIGINT ?
  2. O ${$} está correto?
    (No post mencionado, diz-se que o processo deve matar seu fc, mas seu exemplo usa ${$} em vez de ${BASHPID} .)
por Iskustvo 05.08.2018 / 23:32

0 respostas