Zapisz kod wyjścia na później

15

Mam więc mały skrypt do uruchamiania niektórych testów.

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

Problem polega na tym, że po uruchomieniu skryptu ./testzwróci kod zakończenia powodzenia, nawet jeśli test się nie powiedzie, ponieważ się rm -f *.classpowiedzie.

Jedyny sposób, w jaki mogłem pomyśleć o zrobieniu tego, co chcę, jest dla mnie brzydki:

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

Ale wydaje się, że jest to typowy problem - wykonaj zadanie, posprzątaj, a następnie zwróć kod wyjścia oryginalnego zadania.

Jaki jest najbardziej idiomatyczny sposób na zrobienie tego (w bashu lub ogólnie w powłokach)?

math4tots
źródło

Odpowiedzi:

5

Możesz zawinąć polecenia exiti rmw jedno proste polecenie za pomocą eval:

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

W ten sposób $?wartość przekazywana do exitjest tym, co zostaje przypisane bezpośrednio przed evaluruchomieniem.

mikeserv
źródło
evaljest zawsze ulubieńcem fanów.
mikeserv
23

Poszedłbym z:

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

Po co skakać, kiedy exitjest dostępny?


Możesz użyć trap:

trap 'last_error_code=$?' ERR

Na przykład:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0
muru
źródło
Ach, zgadzam się, że to lepsze niż mój oryginał. Ale nadal wydaje się niezadowalające, że muszę jawnie przechowywać kod wyjścia w zmiennej. Czy nie ma sposobu, aby „wypchnąć” kod wyjścia i „pop” ponownie go później?
math4tots
@ math4tots Wypróbuj aktualizację.
muru
Więc z twoją aktualizacją musiałbym zainicjować kod_błędu na zero, a następnie powrócić na końcu, aby mieć niezerowy kod wyjścia, jeśli jakiekolwiek polecenie zgłosi błąd? To fajna sztuczka, ale myślę, że w przypadku mojego dwuwierszowego skryptu hakerskiego wolę odpowiedź @mikeserv.
math4tots
@ math4tots Zawsze możesz to zrobić exit ${last_error_code:=0}.
muru
@avip po co? Jest już w pojedynczych cudzysłowach, więc zmienna jest oceniana tylko wtedy, gdy wywoływana jest pułapka.
muru
9

O ile wiem, najbliższą rzeczą, którą bash ma do try...finallybloku z języka programowania podobnego do C (co prawdopodobnie byś chciał, gdyby był dostępny) jest trapkonstrukcja, która działa w następujący sposób:

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

Spowoduje to wykonanie polecenia „rm -f * .class” po zakończeniu skryptu. Jeśli masz coś bardziej skomplikowanego do zrobienia, możesz umieścić to w funkcji:

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

Jeśli masz takie skłonności, możesz zmienić to w dość ogólny idiom, który działa mniej więcej tak jak try...catch...finallyblok w C. Coś takiego:

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

Zauważ, że nawiasy ograniczają podpowłokę; w tej konstrukcji tylko podpowłoka kończy działanie, jeśli polecenie się nie powiedzie, a nie cały skrypt. Pamiętaj, że podpowłoki są nieco drogie obliczeniowo, więc nie używaj ich zbyt wielu (setek). W zależności od skryptu możesz osiągnąć ten sam efekt bardziej efektywnie dzięki funkcjom powłoki i trap ... RETURN, ale to zależy od ciebie.

David Z
źródło