Salve o código de saída para mais tarde

15

Então, eu tenho um pequeno script para executar alguns testes.

javac *.java && java -ea Test
rm -f *.class

Agora, o problema é que, quando eu executo o script ./test, ele retornará um código de saída com êxito, mesmo que o teste falhe porque rm -f *.classé bem-sucedido.

A única maneira de pensar em fazê-lo fazer o que quero me parece feia:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
if [ "$test_exit_code" != 0 ] ; then false; fi

Mas isso parece um problema comum - execute uma tarefa, limpe e retorne o código de saída da tarefa original.

Qual é a maneira mais idiomática de fazer isso (no bash ou apenas conchas em geral)?

math4tots
fonte

Respostas:

5

Você pode agrupar os comandos exite rmem um único comando simples com eval:

java ... && java ...
eval "rm -f *.class; exit $?"

O $?valor dessa maneira , quando passado, exité o que for atribuído imediatamente antes da evalexecução.

mikeserv
fonte
evalé sempre um favorito dos fãs.
precisa saber é o seguinte
23

Eu iria com:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
exit "$test_exit_code"

Por que pular quando exitestá disponível?


Você pode usar um trap:

trap 'last_error_code=$?' ERR

Por exemplo:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0
muru
fonte
Ah, eu concordo que é melhor que o meu original. Mas ainda parece insatisfatório que eu tenha que armazenar explicitamente o código de saída em uma variável. Não há como 'empurrar' um código de saída e 'pop' novamente mais tarde?
math4tots
@ math4tots Experimente a atualização.
muru 11/01
Portanto, com sua atualização, eu teria que inicializar last_error_code para zero e retornar no final para ter um código de saída diferente de zero se algum comando gerasse um erro? É um truque legal, mas para o meu script de corte de duas linhas, acho que prefiro a resposta do @mikeserv.
math4tots
@ math4tots Você sempre pode fazer exit ${last_error_code:=0}.
muru
@avip para quê? Já está entre aspas simples, portanto a variável é avaliada apenas quando a armadilha é chamada.
Muru
9

Até onde eu sei, o bash mais próximo de um try...finallybloco de uma linguagem de programação mais parecida com o C (que é o que você provavelmente desejaria se estivesse disponível) é a trapconstrução, que funciona assim:

trap "rm -f *.class" EXIT
javac *.java && java -ea Test

Isso executará "rm -f * .class" quando o script sair. Se você tiver algo mais complexo para fazer, poderá colocá-lo em uma função:

cleanup() {
    ...
}
trap cleanup EXIT
javac *.java && java -ea Test

Se você é tão inclinado, pode transformar isso em um idioma bastante geral que funciona aproximadamente como um try...catch...finallybloco em C. Algo assim:

(
  trap "catch_block; exit" ERR
  trap finally_block EXIT
  # contents of try goes here
)

Observe que os parênteses delimitam um subshell; com essa construção, somente o subshell sai se um comando falhar, não o script inteiro. Lembre-se de que os subconjuntos são um pouco computacionais, portanto, não use muitos (centenas) deles. Dependendo do seu script, você poderá obter o mesmo efeito com mais eficiência com as funções de shell e trap ... RETURN, mas isso depende de você investigar.

David Z
fonte