Możesz przejść do A Quick Fix, ale niekoniecznie jest to najlepsza opcja. Dlatego polecam najpierw przeczytać to wszystko.
rc.local
nie toleruje błędów.
rc.local
nie zapewnia inteligentnego odzyskiwania po błędach. Jeśli dowolne polecenie nie powiedzie się, przestaje działać. Pierwszy wiersz #!/bin/sh -e
powoduje wykonanie go w powłoce wywołanej z -e
flagą. -e
Flaga jest to, co sprawia, że skrypt (w tym przypadku rc.local
) przestanie działać po raz pierwszy polecenia nie powiedzie się w nim.
Chcesz rc.local
się tak zachowywać. Jeśli polecenie nie powiedzie się, nie chcesz, aby kontynuowało działanie z innymi poleceniami uruchamiania, które mogły polegać na powodzeniu.
Więc jeśli jakieś polecenie zawiedzie, kolejne polecenia nie będą działać. Problem polega na tym, że /script.sh
się nie uruchomił (nie, że się nie udało, patrz poniżej), więc najprawdopodobniej jakieś polecenie, zanim się nie powiedzie. Ale który?
Czy to było /bin/chmod +x /script.sh
?
Nie.
chmod
działa dobrze w dowolnym momencie. Pod warunkiem, że system plików, który zawiera, /bin
został podłączony, możesz uruchomić /bin/chmod
. I /bin
jest montowany przed rc.local
uruchomieniami.
Po uruchomieniu jako root /bin/chmod
rzadko kończy się niepowodzeniem. Nie powiedzie się, jeśli plik, na którym działa, jest tylko do odczytu i może zawieść, jeśli system plików, na którym działa, nie obsługuje uprawnień. Prawdopodobnie nie ma tutaj.
Nawiasem mówiąc, sh -e
jest to jedyny powód, dla którego byłby to problem, gdyby się chmod
nie powiódł. Po uruchomieniu pliku skryptu przez jawne wywołanie jego interpretera nie ma znaczenia, czy plik jest oznaczony jako wykonywalny. Tylko gdyby powiedział, /script.sh
że plik wykonywalny ma znaczenie. Ponieważ mówi sh /script.sh
, że tak nie jest (chyba że /script.sh
wywołuje się sam podczas działania, co może się nie powieść, ponieważ nie jest wykonywalny, ale jest mało prawdopodobne, że się wywołuje).
Co więc się nie udało?
sh /home/incero/startup_script.sh
nie powiodło się Prawie na pewno.
Wiemy, że działał, ponieważ został pobrany /script.sh
.
(W przeciwnym razie ważne jest, aby upewnić się, że się uruchomił, na wypadek, gdyby jakoś /bin
nie było w ŚCIEŻCE - rc.local
niekoniecznie ma to samo, PATH
co masz po zalogowaniu. Jeśli /bin
nie byłeś na dobrej rc.local
drodze, to wymagałoby sh
uruchomienia jako /bin/sh
. Ponieważ się uruchomił, /bin
jest w PATH
, co oznacza, że możesz uruchamiać inne polecenia, które się w nim znajdują /bin
, bez pełnego określania ich nazw. Na przykład możesz uruchomić chmod
zamiast tego /bin/chmod
. Jednak zgodnie ze swoim stylem w rc.local
, użyłem w pełni kwalifikowanych nazw dla wszystkich poleceń, z wyjątkiem sh
przypadków, gdy sugeruję ich uruchomienie).
Możemy być prawie pewni, że /bin/chmod +x /script.sh
nigdy nie biegł (lub zobaczysz, że /script.sh
został wykonany). I wiemy, że też sh /script.sh
nie był prowadzony.
Ale zostało pobrane /script.sh
. Udało się! Jak to może zawieść?
Dwa znaczenia sukcesu
Istnieją dwie różne rzeczy, które osoba może mieć na myśli, gdy mówi, że polecenie się powiodło:
- Zrobił to, co chciałeś.
- Poinformowano, że się udało.
I tak jest z porażką. Gdy osoba mówi, że polecenie nie powiodło się, może to oznaczać:
- Nie zrobił tego, co chciałeś.
- Zgłoszono, że się nie udało.
Uruchomienie skryptu sh -e
, na przykład rc.local
, przestanie działać po raz pierwszy, gdy polecenie zgłosi, że nie powiodło się . Nie ma znaczenia, co faktycznie zrobiło polecenie.
Jeśli nie zamierzasz startup_script.sh
zgłaszać awarii, gdy robi to, co chcesz, jest to błąd startup_script.sh
.
- Niektóre błędy uniemożliwiają skryptowi robienie tego, co chcesz. Wpływają na to, co programiści nazywają jego skutkami ubocznymi .
- Niektóre błędy uniemożliwiają skryptowi prawidłowe zgłaszanie, czy się udało. Wpływają na to, co programiści nazywają wartością zwracaną (w tym przypadku jest to status wyjścia ).
Najprawdopodobniej startup_script.sh
zrobił wszystko, co powinien, oprócz tego, że zawiódł.
Jak zgłasza się sukces lub porażkę
Skrypt to lista zer lub więcej poleceń. Każde polecenie ma status wyjścia. Zakładając, że nie ma błędów w uruchamianiu skryptu (na przykład, jeśli interpreter nie mógł odczytać następnego wiersza skryptu podczas uruchamiania), kod wyjścia skryptu to:
0
(sukces), jeśli skrypt był pusty (tzn. nie miał poleceń).
N
, jeśli skrypt zakończył się w wyniku polecenia , gdzie jest kod zakończenia.exit N
N
- Kod wyjścia ostatniego polecenia wykonanego w skrypcie, w przeciwnym razie.
Gdy plik wykonywalny jest uruchamiany, zgłasza swój własny kod wyjścia - nie dotyczą one tylko skryptów. (I technicznie kody wyjścia ze skryptów to kody wyjścia zwracane przez powłoki, które je uruchamiają).
Na przykład, jeśli program C kończy się na exit(0);
lub return 0;
w swojej main()
funkcji, kod 0
jest przekazywany do systemu operacyjnego, który zapewnia go procesowi wywołującemu (którym może być na przykład powłoka, z której program został uruchomiony).
0
oznacza, że program się powiódł. Każda inna liczba oznacza, że się nie udało. (W ten sposób różne liczby mogą czasami odnosić się do różnych przyczyn niepowodzenia programu).
Polecenia przeznaczone do niepowodzenia
Czasami uruchamiasz program z zamiarem niepowodzenia. W takich sytuacjach możesz uznać jego niepowodzenie za sukces, mimo że program nie zgłasza błędu. Na przykład możesz użyć rm
pliku, który podejrzewasz, że już nie istnieje, aby się upewnić, że został usunięty.
Prawdopodobnie dzieje się coś takiego startup_script.sh
, zanim przestanie działać. Ostatnie polecenie, aby uruchomić w skrypcie jest prawdopodobnie zgłaszania awarii (choć jej „awarii” może być całkowicie w porządku, a nawet konieczne), co sprawia, że niepowodzenie raportu skrypt.
Testy oznaczają niepowodzenie
Jednym szczególnym rodzajem polecenia jest test , przez który mam na myśli wykonanie polecenia zamiast jego skutków ubocznych. Oznacza to, że test jest uruchamiany, aby można było zbadać (i wykonać) jego status wyjścia.
Załóżmy na przykład, że zapomniałem, czy 4 jest równe 5. Na szczęście znam skrypty powłoki:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Tutaj test [ -eq 5 ]
kończy się niepowodzeniem, ponieważ okazuje się, że w końcu 4 after 5. To nie znaczy, że test nie działał poprawnie; to zrobiło. Jego zadaniem było sprawdzenie, czy 4 = 5, a następnie zgłoszenie sukcesu, jeśli tak, i niepowodzenia, jeśli nie.
Widzisz, w skryptach powłoki sukces może również oznaczać prawda , a niepowodzenie może oznaczać fałsz .
Mimo że echo
instrukcja nigdy się nie uruchamia, if
blok jako całość zwraca sukces.
Jednak przypuszczam, że napisałem to krócej:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Jest to powszechny skrót. &&
jest wartością logiczną i operatorem. &&
Wyraz, który składa się &&
ze sprawozdania z każdej strony, zwraca false (niepowodzenie), chyba że obie strony powrót prawdziwej (sukces). Tak jak normalnie i .
Jeśli ktoś cię zapyta: „czy Derek poszedł do centrum handlowego i pomyślał o motyle?” i wiesz, że Derek nie poszedł do centrum handlowego, nie musisz się zastanawiać, czy pomyślał o motyle.
Podobnie, jeśli polecenie po lewej stronie &&
nie powiedzie się (fałsz), całe &&
wyrażenie natychmiast zawiedzie (fałsz). Instrukcja po prawej stronie &&
nigdy nie jest uruchamiana.
Tutaj [ 4 -eq 5 ]
biegnie. „Kończy się niepowodzeniem” (zwraca false). Całe &&
wyrażenie zawodzi. echo "Yeah, they're totally the same."
nigdy nie działa. Wszystko zachowało się tak, jak powinno, ale to polecenie zgłasza awarię (mimo że if
powyższy równoważny warunek powyżej informuje o sukcesie).
Gdyby to była ostatnia instrukcja w skrypcie (a skrypt do niej dotarł, zamiast kończyć w pewnym momencie przed nią), cały skrypt zgłosiłby błąd .
Poza tym jest wiele testów. Na przykład są testy z ||
(„lub”). Jednak powyższy przykład powinien wystarczyć do wyjaśnienia, jakie są testy, i umożliwić efektywne korzystanie z dokumentacji w celu ustalenia, czy dane polecenie / polecenie jest testem.
sh
kontra sh -e
ponownie odwiedzone
Od tej #!
linii (patrz też to pytanie ) w górnej części /etc/rc.local
ma sh -e
, system operacyjny uruchamia skrypt tak jakby wywoływana za pomocą polecenia:
sh -e /etc/rc.local
W przeciwieństwie do tego, twoje inne skrypty, takie jak startup_script.sh
, uruchomić bez tej -e
flagi:
sh /home/incero/startup_script.sh
W związku z tym nadal działają, nawet jeśli polecenie w nich zgłasza awarię.
To jest normalne i dobre. rc.local
powinny być wywoływane przy użyciu sh -e
większości skryptów - w tym większości skryptów uruchamianych przez - rc.local
nie powinny.
Pamiętaj tylko o różnicy:
Skrypty są uruchamiane z sh -e
błędem zgłaszania wyjścia, gdy polecenie, które zawierają, kończy działanie zgłaszania błędu.
To tak, jakby skrypt był pojedynczym długim poleceniem składającym się ze wszystkich poleceń w skrypcie połączonych z &&
operatorami.
Skrypty działają z sh
(bez -e
), dopóki nie dotrą do polecenia, które je kończy (wychodzi z nich) lub do samego końca skryptu. Powodzenie lub niepowodzenie każdego polecenia jest w zasadzie nieistotne (chyba że następne polecenie to sprawdzi). Skrypt kończy działanie ze statusem zakończenia ostatniego uruchomienia polecenia.
Pomaganie skryptowi w zrozumieniu, że to wcale nie taka porażka
Jak możesz nie myśleć, że skrypt się nie powiedzie, jeśli nie?
Patrzysz na to, co dzieje się tuż przed uruchomieniem.
Jeśli polecenie nie powiodło się, gdy powinno się powieść, dowiedz się, dlaczego i napraw problem.
Jeśli polecenie nie powiedzie się, a tak właśnie powinno być, należy uniemożliwić propagację tego statusu niepowodzenia.
Jednym ze sposobów na uniknięcie propagacji statusu błędu jest uruchomienie innej komendy, która się powiedzie. /bin/true
nie ma żadnych skutków ubocznych i informuje o sukcesie (tak jak /bin/false
nic nie robi, a także zawodzi).
Innym jest upewnienie się, że skrypt zostanie zakończony przez exit 0
.
To niekoniecznie to samo, exit 0
co na końcu skryptu. Na przykład może istnieć if
blokada, w której skrypt kończy działanie.
Najlepiej wiedzieć, co powoduje, że skrypt zgłasza awarię, zanim zgłosi sukces. Jeśli naprawdę w jakiś sposób zawodzi (w sensie nie robienia tego, co chcesz), tak naprawdę nie chcesz, aby raportował sukces.
Szybka poprawka
Jeśli nie możesz startup_script.sh
pomyślnie zakończyć raportowania wyjścia, możesz zmienić polecenie, rc.local
które je uruchamia, tak aby polecenie raportowało sukces, nawet jeśli startup_script.sh
nie.
Obecnie masz:
sh /home/incero/startup_script.sh
To polecenie ma takie same skutki uboczne (tj. Efekt uboczny uruchamiania startup_script.sh
), ale zawsze zgłasza powodzenie:
sh /home/incero/startup_script.sh || /bin/true
Pamiętaj, że lepiej wiedzieć, dlaczego startup_script.sh
zgłasza awarię i naprawić.
Jak działa szybka poprawka
To jest właściwie przykład ||
testu, lub testu.
Przypuśćmy, że zapytasz, czy wyjąłem śmieci lub wyszczotkowałem sowę. Gdybym wyniósł śmieci, mogę zgodnie z prawdą powiedzieć „tak”, nawet jeśli nie pamiętam, czy wyszczotkowałem sowę.
Polecenie po lewej stronie ||
uruchomień. Jeśli się powiedzie (prawda), to prawa strona nie musi biec. Jeśli więc startup_script.sh
raport się powiedzie, true
polecenie nigdy się nie uruchomi.
Jeśli jednak startup_script.sh
zgłosi błąd [nie wyjąłem śmieci] , wtedy wynik /bin/true
[jeśli wyszczotkowałem sowę] ma znaczenie.
/bin/true
zawsze zwraca sukces (lub prawdziwy , jak to czasem nazywamy). W rezultacie cała komenda się powiedzie i rc.local
może zostać uruchomiona kolejna komenda w .
Dodatkowa uwaga na temat powodzenia / niepowodzenia, prawda / fałsz, zero / zero.
Możesz to zignorować. Możesz to przeczytać, jeśli programujesz w więcej niż jednym języku (tj. Nie tylko skryptowanie powłoki).
Jest to duże zamieszanie dla skrypterów powłoki podejmujących języki programowania, takich jak C, a dla programistów C, którzy podejmują się skryptów powłoki, aby odkryć:
W skryptach powłoki:
- Zwracana wartość
0
oznacza sukces i / lub wartość true .
- Zwracana wartość czegoś innego niż
0
oznacza niepowodzenie i / lub fałsz .
W programowaniu C:
- Zwracana wartość
0
oznacza false .
- Zwracana wartość czegoś innego niż
0
oznacza true .
- Nie ma prostej zasady, co oznacza sukces, a co porażkę. Czasem
0
oznacza sukces, innym razem porażkę, innym razem sumę dwóch liczb, które właśnie dodałeś, wynosi zero. Wartości zwrotne w programowaniu ogólnym są używane do sygnalizowania szerokiej gamy różnych rodzajów informacji.
- Numeryczny status wyjścia programu powinien wskazywać powodzenie lub niepowodzenie zgodnie z regułami skryptowania powłoki. Oznacza to, że nawet jeśli
0
oznacza to fałsz w programie C, nadal powoduje, że Twój program zwraca 0
kod wyjścia, jeśli chcesz, aby zgłosił powodzenie (co powłoka interpretuje jako prawda ).