sai do shell script de um subshell

25

Considere este trecho:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

Normalmente, quando func é chamado, o script será encerrado, que é o comportamento pretendido. No entanto, se for executado em um sub-shell, como em

result='func'

não sairá do script. Isso significa que o código de chamada deve verificar o status de saída da função todas as vezes. Há alguma maneira de evitar isto? É para isso que set -e é?

    
por Ernest A C 18.09.2012 / 18:36

6 respostas

9

Você poderia matar o shell original ( kill $$ ) antes de chamar exit , e isso provavelmente funcionaria. Mas:

  • parece bastante feio para mim
  • ele será quebrado se você tiver um segundo subshell, ou seja, usar um subshell dentro de um subshell.

Em vez disso, você pode usar uma das várias maneiras de repassar um valor na FAQ do Bash . A maioria deles não é tão boa, infelizmente. Você pode estar apenas procurando erros após cada chamada de função ( -e tem muitos problemas ). Ou isso, ou mude para Perl.

    
por 18.09.2012 / 19:52
32

Você pode decidir que o status de saída 77, por exemplo, significa sair de qualquer nível de subshell e fazer

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -E em combinação com ERR traps é um pouco como uma versão aprimorada de set -e , pois permite que você defina seu próprio tratamento de erros.

No zsh, as interceptações ERR são herdadas automaticamente, portanto você não precisa de set -E , você também pode definir interceptações como TRAPERR() funções e modificá-las por $functions[TRAPERR] , como functions[TRAPERR]="echo was here; $functions[TRAPERR]"

    
por 18.09.2012 / 23:19
6

Como uma alternativa para kill $$ , você também pode tentar kill 0 , ele irá funcionar no caso de sub-caixas aninhadas (todos os chamadores e processos laterais receberão o sinal) ... mas ainda é brutal e feio.

    
por 18.09.2012 / 20:18
0

Tente isso ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "shell..."
func $1

echo "subshell..."
result='func $1'

echo "shell..."
echo "result=$result"

Os resultados que obtenho são ...

# test_exitsubshell true
shell...
foo
subshell...
shell...
result=foo
# test_exitsubshell false
shell...
something went wrong

Notas

  • Parametrizado para permitir que o teste if seja true ou false (consulte as duas execuções)
  • Quando o teste if é false , nunca chegamos à subcamada.
por 03.10.2014 / 01:36
0

(resposta específica do Bash) Bash não tem nenhum conceito de exceções. No entanto, com set -o errexit (ou o equivalente: set -e) no nível mais externo, o comando com falha resultará na saída da subshell com um status de saída diferente de zero. Se este for um conjunto de subshells aninhados sem condicionais ao redor da execução dessas subshells, ele efetivamente "acumulará" todo o script e sairá.

Isso pode ser complicado ao tentar incluir bits de vários códigos bash em um script maior. Um pedaço de bash pode funcionar muito bem por conta própria, mas quando executado sob errexit (ou sem errexit), se comporta de maneiras inesperadas.

[192.168.13.16 (f0f5e19e) ~ 22:58:22]# bash -o errexit /tmp/foo
something went wrong
[192.168.13.16 (f0f5e19e) ~ 22:58:31]# bash /tmp/foo
something went wrong
But we got here anyways
[192.168.13.16 (f0f5e19e) ~ 22:58:37]# cat /tmp/foo
#!/bin/bash
stop () {
    echo "${1}"
    exit 1
}

if false; then
    echo "foo"
else
    (
        stop "something went wrong"
    )
    echo "But we got here anyways"
fi
[192.168.13.16 (f0f5e19e) ~ 22:58:40]#
    
por 10.12.2018 / 23:54
-2

Meu exemplo para sair em um forro:

COMAND || ( echo "ERROR – executing COMAND, exiting..." ; exit 77 );[ "$?" -eq 77 ] && exit
    
por 19.07.2017 / 11:04