Ostatnio natknąłem się na listę kodów wyjścia o specjalnych znaczeniach z Advanced Bash-Scripting Guide. Odnoszą się do tych kodów jako zastrzeżone i zalecają:
Zgodnie z powyższą tabelą kody wyjścia 1-2, 126-165 i 255 mają specjalne znaczenie i dlatego należy ich unikać w przypadku parametrów wyjścia określonych przez użytkownika.
Jakiś czas temu napisałem skrypt, który używał następujących kodów statusu wyjścia:
- 0 - sukces
- 1 - niepoprawna nazwa hosta
- 2 - podano nieprawidłowe argumenty
- 3 - niewystarczające uprawnienia użytkownika
Kiedy pisałem skrypt, nie znałem żadnych specjalnych kodów wyjścia, więc po prostu zacząłem od 1 dla pierwszego warunku błędu i zwiększałem status wyjścia dla każdego kolejnego typu błędu.
Napisałem skrypt z zamiarem, że na późniejszym etapie może on zostać wywołany przez inne skrypty (które mogą sprawdzić niezerowe kody wyjścia). Tak naprawdę jeszcze tego nie zrobiłem; jak dotąd uruchomiłem skrypt tylko z mojej interaktywnej powłoki (Bash) i zastanawiałem się, co / czy problemy mogą być spowodowane przez użycie moich niestandardowych kodów wyjścia. Jak istotna / ważna jest rekomendacja z Advanced Bash-Scripting Guide?
Nie mogłem znaleźć żadnej potwierdzającej porady w dokumentacji Bash; jej sekcja na temat statusu wyjścia po prostu zawiera listę kodów wyjścia używanych przez Bash, ale nie stwierdza, że żaden z nich jest zarezerwowany, ani ostrzega przed użyciem ich do własnych skryptów / programów.
źródło
Odpowiedzi:
Podjęto szereg prób standaryzacji znaczenia kodów zakończenia procesu. Oprócz tego, o którym wspominasz, wiem o:
BSD mają
sysexits.h
znaczenie dla wartości od 64 wzwyż.grep
Dokumenty GNU, że kod wyjścia 0 oznacza, że znaleziono co najmniej jedno dopasowanie, 1 oznacza, że nie znaleziono pasujących, a 2 oznacza wystąpienie błędu We / Wy; ta konwencja jest oczywiście przydatna również w przypadku innych programów, dla których rozróżnienie między „nic nie poszło nie tak, ale nic nie znalazłem” i „wystąpił błąd we / wy” jest znaczący.Wiele implementacji funkcji biblioteki C
system
używa kodu wyjścia 127, aby wskazać, że program nie istnieje lub nie uruchomił się.W systemie Windows
NTSTATUS
kody (które są niewygodnie rozrzucone w 32-bitowej przestrzeni liczbowej) mogą być używane jako kody wyjścia, szczególnie te, które wskazują, że proces został zakończony z powodu katastrofalnego zachowania (npSTATUS_STACK_OVERFLOW
.).Nie możesz liczyć na to, że dany program będzie przestrzegał określonej konwencji. Jedyną niezawodną regułą jest to, że kod wyjścia 0 to sukces, a wszystko inne to jakaś awaria. (Należy pamiętać, że C89 nie
EXIT_SUCCESS
ma gwarancji, że ma wartość zero; jednak musi zachowywać się identycznie, nawet jeśli wartości nie są takie same.)exit(0)
exit(EXIT_SUCCESS)
źródło
Żaden kod wyjścia nie ma specjalnego znaczenia, ale wartość w
$?
może mieć specjalne znaczenie.Problemem jest sposób, w jaki Bourne Shell i ksh93 przetwarzają i przekazują kody wyjścia oraz sytuacje błędów do zmiennej powłoki
$?
. W przeciwieństwie do tego, co podajesz, tylko następujące wartości$?
mają specjalne znaczenie:Ponadto istnieje nieokreślona powłoka i specyficzny dla platformy zakres
$?
kodów> 128, który jest zarezerwowany dla programu, który został przerwany przez sygnał:Inne wartości nie powodują problemów, ponieważ można je odróżnić od
$?
wartości specjalnych dla powłoki .W szczególności wartości 1 i 2 nie są używane w szczególnych warunkach, ale są jedynie kodami wyjścia używanymi przez wbudowane polecenia, które mogłyby działać tak samo, gdy nie są wbudowanymi. Wygląda na to, że wskaźnik do dostarczonego przez ciebie skryptu bash nie jest dobrym podręcznikiem, ponieważ zawiera tylko kody używane przez bash bez komentowania, czy określony kod jest specjalną wartością, której należy unikać w przypadku własnych skryptów.
Nowsze wersje Bourne Shell używają
waitid()
zamiastwaitpid()
czekać na zakończenie programu, awaitid()
(wprowadzony 1989 dla SVr4) używa lepszego interfejsu syscall (podobnego do tego, który UNOS używał już w 1980 roku).Ponieważ nowsze wersje Bourne Shell kodują przyczynę wyjścia w osobnej zmiennej
${.sh.code}
/${.sh.codename}
niż kod wyjścia znajdujący się w${.sh.status}
/${.sh.termsig}
, patrz http://schillix.sourceforge.net/man/man1/bosh.1.html , kod wyjścia nie jest przeciążony ze specjalnymi stanami, a w wyniku użycia `waitid (), powłoka Bourne'a obsługuje teraz zwracanie wszystkich 32 bitów kodu wyjścia - nie tylko niskich 8 bitów.BTW: uważaj, aby nie
exit(256)
lub podobnie do programu C lub skryptu powłoki, ponieważ powoduje$?
to interpretację jako 0 w klasycznej powłoce.źródło
waitid()
błędu pod koniec maja. Ludzie FreeBSD naprawili problem w ciągu 20 godzin, ludzie Linuksa nie są zainteresowani naprawieniem swojego błędu. ... a ludzie z Cygwin mówią, że są kompatybilni z błędami w Linuksie ;-)_exit
. Podaj link do raportu o błędzie FreeBSD, o którym mówisz, być może nie rozumiem opisanego problemu.${.sh.}
zmiennych. Prawdą jest jednak, że mówisz „Bourne”, a nie „wywodzące się z Bourne'a” (chociaż zawierasz ksh93)./bin/sh
mógł polegać na konsekwentnym zachowaniu tych specjalnych kodów wyjścia na różnych platformach, co nie jest prawdą. (Nie dbam o to, czy jakikolwiek konkretny system/bin/sh
można uznać za „prawdziwą powłokę Bourne'a”. O wiele ważniejsze jest, aby wiedzieć, że nic z tego nie jest w POSIX-ie i że większość rzeczy, które wymieniasz jako „prawdziwe systemy uniksowe”, nie „ i tak zapewnia zgodność z POSIX/bin/sh
.)W przypadku skryptów powłoki czasami umieszczam w źródle odpowiednik powłoki
sysexist.h
z kodami wyjścia zarezerwowanymi dla powłoki (z prefiksemS_EX_
), które nazwałemexit.sh
Jest to w zasadzie:
I można wygenerować za pomocą:
Nie używam go jednak dużo, ale używam funkcji powłoki, która odwraca kody błędów na ich formaty ciągów. Nazwałem to
exit2str
. Zakładając, że nazwałeś powyższyexit.sh
generatorexit.sh.sh
, kod dlaexit2str
można wygenerować za pomocą (exit2str.sh.sh
):Używam tego w
PS1
mojej interaktywnej powłoce, dzięki czemu po każdym uruchomieniu polecenia widzę jego status wyjścia i ciąg znaków (jeśli ma znany ciąg znaków):Aby je uzyskać, potrzebujesz klucza źródłowego dla funkcji exit2str:
a następnie użyj go w swoim,
~/.bashrc
aby zapisać i przetłumaczyć kod wyjścia w każdym wierszu polecenia i wyświetlić go jako znak zachęty (PS1
):Jest to całkiem przydatne do obserwowania, jak niektóre programy przestrzegają konwencji kodu wyjścia, a inne nie, do poznawania konwencji kodu wyjścia lub po prostu dla łatwiejszego zobaczenia, co się dzieje. Używając go od jakiegoś czasu, mogę powiedzieć, że wiele systemowych skryptów powłoki przestrzega konwencji.
EX_USAGE
jest szczególnie dość powszechny, choć inne kody niewiele. Od czasu do czasu staram się przestrzegać konwencji, chociaż zawsze$S_EX_ANY
leniwi ludzie (jestem jednym z nich).źródło
Dopóki dokumentujesz swoje kody wyjścia, abyś pamiętał je za rok, kiedy będziesz musiał wrócić i dostosować skrypt, nic ci nie będzie. Pomysł „zarezerwowanych kodów wyjścia” nie ma już zastosowania poza stwierdzeniem, że zwykle stosuje się go
0
jako kod sukcesu, a cokolwiek innego jako kod błędu.źródło
Najlepsze referencje, jakie mogłem znaleźć, to: http://tldp.org/LDP/abs/html/exitcodes.html
Według tego:
1
jest ogólną regułą dotyczącą błędów i zawsze widziałem, że jest używana w przypadku błędów zdefiniowanych przez użytkownika.2
służy do niewłaściwego użycia wbudowanych powłok, takich jak błąd składniowyAby bezpośrednio odpowiedzieć na twoje pytanie, skrypt będzie działał poprawnie przy użyciu zastrzeżonych kodów błędów, będzie działał zgodnie z oczekiwaniami, zakładając, że obsłużysz błąd na podstawie kodu błędu = 1/2/3.
Jednak może być mylące, jeśli spotkasz kogoś, kto zna i używa zastrzeżonych kodów błędów, co wydaje się dość rzadkie.
Inną dostępną opcją jest powtórzenie błędu, jeśli taki istnieje, a następnie wyjście, przy założeniu, że skrypt jest zgodny z konwencją Linuksa, zgodnie z którą „brak wiadomości to dobra wiadomość”, a echo nie ma żadnego sukcesu.
źródło
W oparciu o odpowiedzi, które otrzymałem (trudno było wybrać jedną z pozostałych), wskazanie pewnych rodzajów błędów przy użyciu kodu wyjścia, którego używa Bash , nie jest szkodliwe . Bash (lub jakakolwiek inna powłoka uniksowa) nie zrobi nic specjalnego (takiego jak uruchamianie programów obsługi wyjątków), jeśli skrypt użytkownika zakończy działanie z jednym z tych kodów błędów.
Wygląda na to, że autor Advanced Bash-Scripting Guide zgadza się z próbami standaryzacji kodów wyjścia (
sysexits.h
) przez BSD i po prostu zaleca, aby użytkownicy piszący skrypty powłoki nie podawali już kodów wyjścia, które są w konflikcie ze wstępnie zdefiniowanymi kodami wyjścia w użyciu, tj. ograniczają swoje niestandardowe kody wyjścia do 50 dostępnych kodów stanu z zakresu 64-113.Doceniam pomysł (i uzasadnienie), ale wolałbym, gdyby autor był bardziej wyraźny, że ignorowanie porady nie jest szkodliwe - oprócz przypadków, w których konsument skryptu sprawdza błędy, takie jak cytowany przykład 127 (
command not found
).Odpowiednie specyfikacje POSIX
Zbadałem, co POSIX ma do powiedzenia na temat kodów wyjścia, a specyfikacja POSIX wydaje się zgadzać z autorem Advanced Bash-Scripting Guide. Przytoczyłem odpowiednie specyfikacje POSIX (moje wyróżnienie):
Status wyjścia dla poleceń
exit
użytecznośćDalsza informacja
Z tego, co warto, udało mi się zweryfikować wszystkie kody wyjścia poza specjalnymi znaczeniami oprócz jednego . Ta tabela kodów wyjścia jest przydatna, ponieważ zawiera więcej szczegółów - i przykłady generowania kodów błędów udokumentowanych w odnośniku Bash .
Próba wygenerowania statusu wyjścia 128
Korzystając z wersji Bash 3.2.25 i 4.2.46, próbowałem wyrzucić
128 Invalid argument to exit
błąd, ale za każdym razem otrzymywałem 255 (Status wyjścia poza zakresem). Na przykład, jeśliexit 3.14159
jest wykonywany jako część skryptu powłoki lub w interaktywnej powłoce podrzędnej, powłoka kończy działanie z kodem255
:Dla jeszcze większej zabawy próbowałem również uruchomić prosty program w C, ale w tym przypadku wydaje się, że
exit(3)
funkcja po prostu przekształciła liczbę zmiennoprzecinkową na int (w tym przypadku 3) przed wyjściem:źródło