Se eu usar trap
como descrito, por exemplo, em http://linuxcommand.org/wss0160.php#trap para capturar ctrl-c (ou similar) e limpeza antes de sair, estou alterando o código de saída retornado.
Agora, isso provavelmente não fará diferença no mundo real (por exemplo, porque os códigos de saída não são portáteis e, além disso, nem sempre são ambíguos, conforme discutido no Código de saída padrão quando o processo é finalizado? ), Mas ainda estou me perguntando se existe algum realmente não há como evitar isso e retornar o código de erro padrão para scripts interrompidos?
Exemplo (no bash, mas minha pergunta não deve ser considerada específica do bash):
#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _
Resultado:
$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT
EXIT
$ echo $?
1
(Editado para remover para torná-lo mais compatível com POSIX.)
(Editado novamente para torná-lo um script bash, minha pergunta não é específica do shell.)
Editado para usar o "INT" portátil para interceptar a favor do "SIGINT" não portátil.
Editado para remover chaves inúteis e adicionar uma solução em potencial.
Atualizar:
Eu o resolvi agora, simplesmente saindo com alguns códigos de erro codificados e capturando EXIT. Isso pode ser problemático em alguns sistemas, porque o código de erro pode ser diferente ou a armadilha EXIT não é possível, mas no meu caso está tudo bem.
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
read
para ler o coprocesso atual etrap cmd SIGINT
não funcionará, pois o padrão diz que você deve usartrap cmd INT
.read -p
lê as entradas do coprocesso atual.Respostas:
Tudo que você precisa fazer é alterar o manipulador EXIT dentro do manipulador de limpeza. Aqui está um exemplo:
fonte
trap cleanup INT
vez detrap cleanup EXIT
?Na verdade, interromper o interno do bash
read
parece ser um pouco diferente de interromper um comando executado pelo bash. Normalmente, quando você digitatrap
,$?
está definido e você pode preservá-lo e sair com o mesmo valor:Se o seu script for interrompido ao executar um comando como
sleep
ou mesmo um internowait
, você veráe o código de saída é 130. No entanto,
read -p
parece que$?
é 0 (na minha versão do bash 4.3.42 de qualquer maneira).A manipulação de sinais durante
read
pode estar em andamento, de acordo com o arquivo de alterações no meu release ... (/ usr / share / doc / bash / CHANGES)fonte
read
, conforme observado no arquivo CHANGES (adicionado à minha resposta). Portanto, pode ser um trabalho em andamento.128 + signo
como código de saída para sinais, o ksh93 usa256 + signo
. POSIX diz: algo acima de 128 ....Qualquer código de saída de sinal usual estará disponível na
$?
entrada do manipulador de trap:Se houver um trap EXIT separado, você poderá usar a mesma metodologia: mantenha imediatamente o status de saída passado da limpeza do manipulador de sinais (se houver), e retorne o status de saída salvo.
fonte
local
não é POSIX embora.{ba,z}sh
. AFAIK, é o melhor que pode ser feito, independentemente.$?
é 1 qualquer que seja o sinal recebido).Basta retornar um código de erro que não é suficiente para simular uma saída pelo SIGINT. Estou surpreso que ninguém tenha mencionado isso até agora. Leitura adicional: https://www.cons.org/cracauer/sigint.html
A maneira correta é:
Isso funciona com o Bash, Dash e zsh. Para maior portabilidade, você precisa usar especificações de sinal numérico (por outro lado, o zsh espera um parâmetro de string para o
kill
comando ...)Observe também o tratamento especial do
EXIT
sinal. Isso ocorre porque alguns shells (ou seja, o Bash) também executam trapsEXIT
para qualquer sinal (após possíveis traps definidos nesse sinal). A redefinição daEXIT
armadilha impede isso.Executando código apenas na saída "normal"
A verificação
[ $sig = EXIT ]
permite executar o código apenas na saída normal (sem sinal). No entanto, todos os sinais precisam ter armadilhas que, por fim, redefinem a armadilhaEXIT
;normal_exit_only_cleanup
também será chamado pelos sinais que não o fazem. Também será executado por uma saídaset -e
. Isso pode ser corrigido capturandoERR
(que não é suportado pelo Dash) e adicionando uma verificação[ $sig = ERR ]
antes dokill
.Versão simplificada do Bash-only
Por outro lado, esse comportamento significa que no Bash você pode simplesmente fazer
para executar apenas algum código de limpeza e manter o status de saída.
editado
O comportamento elaborado de Bash de "EXIT prende tudo"
Remova o sinal KILL, que não pode ser preso
Remover prefixos SIG dos nomes dos sinais
Não tente
kill -s EXIT
Considerar
set -e
/ ERRfonte
SIGINT
é a maneira de desligá-lo e pode decidir sair com 0 se conseguir terminar sem erro ou outro código de erro se não desligar corretamente. Caso em questão:top
. Executetop; echo $?
e pressione Ctrl-C. O status despejado na tela será 0.