Przechwyć kod wyjścia polecenia exit

10

Mam to w skrypcie bash:

exit 3;

exit_code="$?"

if [[ "$exit_code" != "0" ]]; then
    echo -e "${r2g_magenta}Your r2g process is exiting with code $exit_code.${r2g_no_color}";
    exit "$exit_code";
fi

Wygląda na to, że zakończy działanie zaraz po poleceniu wyjścia, co ma sens. Zastanawiałem się, czy istnieje jakieś proste polecenie, które może dostarczyć kod wyjścia bez wychodzenia od razu?

Miałem zgadywać:

exec exit 3

ale daje komunikat o błędzie: exec: exit: not found. Co mogę zrobić? :)

G-Man mówi „Przywróć Monikę”
źródło
1
Tak, exec exit 3nie ma bueno, rozumiem"exec: exit: not found"
7
Nie rozumiem pytania. Dlaczego nie ustawić exit_code=3i exit 3całkowicie wyeliminować linię?
wjandrea
@wjandrea jest bardziej koncepcyjnym pytaniem niż praktycznym
2
To wciąż nie ma dla mnie sensu. Dlaczego miałby istnieć kod wyjścia, jeśli tak naprawdę nie wyjdziesz?
Barmar,
1
@Barmar każdy proces ma kod wyjścia. Większość osób, które próbują odpowiedzieć na pytanie, interpretuje pytanie w ten sposób, że „co mogę zastąpić„ wyjściem 3 ”w skrypcie, aby ustawić $?zmienną, ale nie wychodzi z tego skryptu?
icarus

Odpowiedzi:

32

Jeśli masz skrypt, który uruchamia jakiś program i sprawdza status wyjścia programu (z  $?), i chcesz przetestować ten skrypt, robiąc coś , co powoduje $?ustawienie pewnej znanej wartości (np.  3), Po prostu zrób

(exit 3)

Nawiasy tworzą pod-powłokę. Następnie exitpolecenie powoduje zamknięcie podpowłoki z określonym statusem wyjścia.

G-Man mówi „Przywróć Monikę”
źródło
Ponadto do celów debugowania równie łatwo byłoby ustawić exit_code="3"do testowania
Centimane
1
Tak, Wjandrea zwrócił na to uwagę wczoraj.
G-Man mówi „Przywróć Monikę”
12

exitjest wbudowany w bash, więc nie możesz tego execzrobić. Per instrukcji atakujących za :

Status wyjścia Bash to status wyjścia ostatniego polecenia wykonanego w skrypcie. Jeśli nie zostaną wykonane żadne polecenia, status wyjścia to 0.

Podsumowując, powiedziałbym, że jedyną opcją jest przechowywanie pożądanego statusu wyjścia w zmiennej, a następnie, exit $MY_EXIT_STATUSgdy jest to właściwe.

solarshado
źródło
hmmm co myślisz o pomyśle @ G-mana?
2
Może źle zrozumiałem, co próbujesz osiągnąć. Jeśli próbujesz tylko ustawić $?(choć nie jestem do końca pewien, dlaczego), wydaje się to solidną odpowiedzią. Jeśli chcesz tylko ustawić na nieudaną wartość, falsejest inną opcją.
solarshado,
10

Możesz napisać funkcję, która zwraca status podany jako argument lub 255jeśli nie podano żadnego. (Nazywam to, retponieważ „zwraca” swoją wartość).

ret() { return "${1:-255}"; }

i użyj retzamiast twojego połączenia z exit. Pozwala to uniknąć nieefektywności tworzenia podpowłoki w obecnie akceptowanej odpowiedzi.

Niektóre pomiary.

time bash -c 'for i in {1..10000} ; do (exit 3) ; done ; echo $?'

na moim komputerze zajmuje około 3,5 sekundy.

 time bash -c 'ret(){ return $1 ; } ; for i in {1..10000} ; do ret 3 ; done ; echo $?'

na mojej maszynie zajmuje około 0,051 sekundy, 70 razy szybciej. Ustawienie domyślnej obsługi wciąż pozostawia ją 60 razy szybciej. Oczywiście pętla ma pewne narzuty. Jeśli zmienię ciało pętli na po prostu :lub truewtedy czas zostanie zmniejszony o połowę do 0,025, całkowicie pusta pętla jest niepoprawną składnią. Dodanie ;:do pętli pokazuje, że to minimalne polecenie zajmuje 0,007 sekundy, więc narzut pętli wynosi około 0,018. Odjęcie tego narzutu od dwóch testów pokazuje, że retrozwiązanie jest ponad 100 razy szybsze.

Oczywiście jest to syntetyczny pomiar, ale wszystko się sumuje. Jeśli sprawisz, że wszystko będzie 100 razy wolniejsze, niż jest to konieczne, otrzymasz wolne systemy. 0,0

Ikar
źródło
2
@iBug Dodatkowa przestrzeń nie jest potrzebna.
icarus
Dobra uwaga na temat nieefektywności tworzenia podpowłoki. Czytałem, że niektóre pociski mogą być wystarczająco inteligentne, aby zoptymalizować rozwidlenie w takich przypadkach, ale ten bash nie jest jednym z nich.
G-Man mówi „Przywróć Monikę”
3

O exec exit 3… próbowałby uruchomić zewnętrzną komendę o nazwie exit, ale jej nie ma, więc pojawia się błąd. Musi to być polecenie zewnętrzne zamiast wbudowanego w powłokę, ponieważ całkowicie exec zastępuje powłokę. Co oznacza również, że nawet gdyby wywołano zewnętrzne polecenie exit, exec exit 3nie wróci, aby kontynuować skrypt powłoki, ponieważ powłoki już nie będzie .

ilkkachu
źródło
1
Myślę, że mógłbyś to zrobić exec bash -c "exit 3", ale w tej chwili nie mogę wymyślić żadnego powodu, aby to zrobić, a nie tylko exit 3.
David Z
1
@DavidZ, w każdym razie exec'ing lub justing exit' zatrzyma skrypt, co nie wyglądało tak, jak chciałoby to pytanie.
ilkkachu
3

Możesz to zrobić za pomocą Awk:

awk 'BEGIN{exit 9}'

Lub Sed:

sed Q9 /proc/stat
Steven Penny
źródło
... lub ze skorupą:sh -c 'exit 9'
ilkkachu
1
@ilkkachu, jeśli masz zamiar to zrobić, równie dobrze możesz zrobić (exit 9)w przyjętej odpowiedzi
Steven Penny