Sie können bis hinunter zu A Quick Fix springen, dies ist jedoch nicht unbedingt die beste Option. Deshalb empfehle ich, dies alles zuerst zu lesen.
rc.local
toleriert keine Fehler.
rc.local
bietet keine Möglichkeit, Fehler intelligent zu beheben. Wenn ein Befehl fehlschlägt, wird er nicht mehr ausgeführt. Die erste Zeile #!/bin/sh -e
bewirkt, dass sie in einer Shell ausgeführt wird, die mit dem -e
Flag aufgerufen wird . Das -e
Flag bewirkt, dass ein Skript (in diesem Fall rc.local
) beim ersten Fehlschlagen eines Befehls nicht mehr ausgeführt wird.
Du willst rc.local
dich so verhalten. Wenn ein Befehl fehlschlägt, möchten Sie nicht, dass er mit allen anderen Startbefehlen fortgesetzt wird, die möglicherweise darauf beruhen, dass der Befehl erfolgreich ausgeführt wurde.
Wenn also ein Befehl fehlschlägt, werden die nachfolgenden Befehle nicht ausgeführt. Das Problem hier ist, dass /script.sh
es nicht ausgeführt wurde (nicht, dass es fehlgeschlagen ist, siehe unten), also höchstwahrscheinlich ein Befehl, bevor es fehlgeschlagen ist. Aber welcher?
War es /bin/chmod +x /script.sh
?
Nein.
chmod
läuft jederzeit einwandfrei. Vorausgesetzt, das Dateisystem, das es enthält, /bin
wurde bereitgestellt, können Sie ausführen /bin/chmod
. Und /bin
wird vor rc.local
Läufen montiert .
Wenn es als root ausgeführt wird, /bin/chmod
schlägt es selten fehl. Es schlägt fehl, wenn die Datei, auf der es ausgeführt wird, schreibgeschützt ist, und schlägt möglicherweise fehl, wenn das Dateisystem, auf dem es ausgeführt wird, keine Berechtigungen unterstützt. Beides ist hier nicht wahrscheinlich.
Übrigens sh -e
ist das der einzige Grund, warum es ein Problem wäre, wenn es chmod
fehlschlägt. Wenn Sie eine Skriptdatei ausführen, indem Sie ihren Interpreter explizit aufrufen, spielt es keine Rolle, ob die Datei als ausführbar markiert ist. Nur wenn es heißt, /script.sh
würde das ausführbare Bit der Datei von Bedeutung sein. Da heißt sh /script.sh
es nicht, es sei denn, es /script.sh
ruft sich selbst auf, während es ausgeführt wird, was daran scheitern könnte, dass es nicht ausführbar ist, aber es ist unwahrscheinlich, dass es sich selbst aufruft.
Was ist also gescheitert?
sh /home/incero/startup_script.sh
gescheitert. Fast sicher.
Wir wissen, dass es lief, weil es heruntergeladen wurde /script.sh
.
(Sonst wäre es wichtig, sicherzustellen, dass es lief, irgendwie in Fall /bin
war nicht in PATH - rc.local
nicht unbedingt das gleiche PATH
. Wie Sie haben , wenn Sie angemeldet sind Wenn /bin
war nicht rc.local
‚s Weg, diese würde erfordern , sh
wie ausgeführt werden /bin/sh
. da es lief, /bin
in ist PATH
, was bedeutet , dass Sie andere Befehle ausführen können , die sich befinden /bin
, ohne ihre Namen voll qualifizierenden. Sie können beispielsweise nur ausführen können , chmod
statt /bin/chmod
. Doch mit Stil im Einklang in rc.local
, habe ich voll qualifizierte Namen für alle Befehle mit Ausnahme verwendet sh
, wann immer ich vorschlage Sie sie ausführen.)
Wir können uns ziemlich sicher sein, dass es /bin/chmod +x /script.sh
nie gelaufen ist (oder Sie würden sehen, dass /script.sh
das ausgeführt wurde). Und wir wissen, dass sh /script.sh
es auch nicht gelaufen ist.
Aber es wurde heruntergeladen /script.sh
. Es ist gelungen! Wie konnte es scheitern?
Zwei Bedeutungen des Erfolgs
Es gibt zwei verschiedene Dinge, die eine Person bedeuten könnte, wenn sie sagt, dass ein Befehl erfolgreich war:
- Es hat getan, was Sie wollten.
- Es wurde berichtet, dass es gelungen ist.
Und so ist es zum Scheitern verurteilt. Wenn eine Person sagt, ein Befehl sei fehlgeschlagen, könnte dies bedeuten:
- Es hat nicht getan, was Sie wollten.
- Es hat berichtet, dass es fehlgeschlagen ist.
Ein Skript, das mit ausgeführt sh -e
wird rc.local
, wird angehalten, wenn ein Befehl zum ersten Mal meldet, dass es fehlgeschlagen ist . Es macht keinen Unterschied, was der Befehl tatsächlich tat.
Wenn Sie nicht beabsichtigen startup_script.sh
, einen Fehler zu melden, wenn er das tut, was Sie wollen, ist dies ein Fehler in startup_script.sh
.
- Einige Fehler verhindern, dass ein Skript das tut, was Sie möchten. Sie beeinflussen, was Programmierer ihre Nebenwirkungen nennen .
- Und einige Fehler verhindern, dass ein Skript korrekt meldet, ob es erfolgreich war oder nicht. Sie beeinflussen, was Programmierer als Rückgabewert bezeichnen (was in diesem Fall ein Exit-Status ist ).
Es ist sehr wahrscheinlich, dass startup_script.sh
es alles getan hat, außer dass gemeldet wurde, dass es fehlgeschlagen ist.
Wie Erfolg oder Misserfolg gemeldet wird
Ein Skript ist eine Liste von null oder mehr Befehlen. Jeder Befehl hat einen Beendigungsstatus. Angenommen, es gibt keine Fehler bei der tatsächlichen Ausführung des Skripts (wenn der Interpreter beispielsweise die nächste Zeile des Skripts während der Ausführung nicht lesen konnte), lautet der Beendigungsstatus eines Skripts wie folgt:
0
(Erfolg) wenn das Skript leer war (dh keine Befehle hatte).
N
Wenn das Skript aufgrund des Befehls beendet wurde , finden Sie hier einen Exit-Code.exit N
N
- Der Exit-Code des letzten Befehls, der ansonsten im Skript ausgeführt wurde.
Wenn eine ausführbare Datei ausgeführt wird, meldet sie ihren eigenen Exit-Code - nicht nur für Skripte. (Und technisch gesehen sind Exit-Codes aus Skripten die Exit-Codes, die von den Shells zurückgegeben werden, die sie ausführen.)
Wenn beispielsweise ein C-Programm mit exit(0);
oder return 0;
in seiner main()
Funktion endet , wird der Code 0
dem Betriebssystem übergeben, das ihn für den aufrufenden Prozess bereitstellt (bei dem es sich beispielsweise um die Shell handeln kann, von der aus das Programm ausgeführt wurde).
0
Bedeutet, dass das Programm erfolgreich war. Jede andere Zahl bedeutet, dass es fehlgeschlagen ist. (Auf diese Weise können sich unterschiedliche Zahlen manchmal auf unterschiedliche Gründe beziehen, warum das Programm fehlgeschlagen ist.)
Befehle, die fehlschlagen sollen
Manchmal führen Sie ein Programm mit der Absicht aus, dass es fehlschlägt. In diesen Situationen können Sie den Fehler als Erfolg ansehen, auch wenn es kein Fehler ist, den das Programm als Fehler meldet. Sie können beispielsweise rm
eine Datei verwenden, von der Sie vermuten, dass sie noch nicht vorhanden ist, um sicherzustellen, dass sie gelöscht wird.
So etwas passiert wahrscheinlich in startup_script.sh
, kurz bevor es aufhört zu laufen. Der letzte Befehl, der im Skript ausgeführt wird, meldet wahrscheinlich einen Fehler (auch wenn sein "Fehler" völlig in Ordnung oder sogar notwendig ist), wodurch das Skript einen Fehler meldet.
Tests scheitern
Eine spezielle Art von Befehl ist ein Test , mit dem ich einen Befehl meine, der eher auf seinen Rückgabewert als auf seine Nebenwirkungen hin ausgeführt wird. Das heißt, ein Test ist ein Befehl, der ausgeführt wird, damit sein Beendigungsstatus überprüft werden kann (und auf den reagiert werden kann).
Angenommen, ich vergesse, wenn 4 gleich 5 ist. Glücklicherweise kenne ich mich mit Shell-Skripten aus:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Hier [ -eq 5 ]
schlägt der Test fehl, weil er immerhin 4 ≠ 5 ergibt. Das bedeutet nicht, dass der Test nicht korrekt durchgeführt wurde. es tat. Es war Aufgabe zu überprüfen, ob 4 = 5, dann Erfolg zu melden, und Fehler, wenn nicht.
Sie sehen, in Shell-Skripten kann Erfolg auch wahr und Misserfolg auch falsch bedeuten .
Obwohl die echo
Anweisung niemals ausgeführt wird, gibt der if
gesamte Block Erfolg zurück.
Angenommen, ich hätte es kürzer geschrieben:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Dies ist eine übliche Abkürzung. &&
ist ein boolean und Operator. Ein &&
Ausdruck, der aus &&
Anweisungen auf beiden Seiten besteht, gibt false (Fehler) zurück, es sei denn, beide Seiten geben true (Erfolg) zurück. Genau wie ein normaler und .
Wenn dich jemand fragt: "Ist Derek ins Einkaufszentrum gegangen und hat an einen Schmetterling gedacht?" und du weißt, Derek ist nicht ins Einkaufszentrum gegangen, du musst dir keine Gedanken machen, ob er an einen Schmetterling denkt.
Wenn der Befehl links von &&
fehlschlägt (false), &&
schlägt der gesamte Ausdruck sofort fehl (false). Die Anweisung auf der rechten Seite von &&
wird niemals ausgeführt.
Hier [ 4 -eq 5 ]
läuft. Es "schlägt fehl" (Rückgabe falsch). Der ganze &&
Ausdruck versagt also. echo "Yeah, they're totally the same."
läuft nie. Alles hat sich so verhalten, wie es sein sollte, aber dieser Befehl meldet einen Fehler (obwohl die ansonsten äquivalente if
Bedingung oben einen Erfolg meldet).
Wenn dies die letzte Anweisung in einem Skript wäre (und das Skript darauf zugreifen würde, anstatt irgendwann davor zu enden), würde das gesamte Skript einen Fehler melden .
Daneben gibt es viele Tests. Beispielsweise gibt es Tests mit ||
("oder"). Das obige Beispiel sollte jedoch ausreichen, um zu erklären, was Tests sind, und es Ihnen zu ermöglichen, mithilfe der Dokumentation effektiv festzustellen, ob eine bestimmte Anweisung / ein bestimmter Befehl ein Test ist.
sh
vs. sh -e
Revisited
Da die #!
Zeile (siehe auch diese Frage ) oben /etc/rc.local
hat sh -e
, führt das Betriebssystem das Skript so aus, als ob es mit dem Befehl aufgerufen worden wäre:
sh -e /etc/rc.local
Im Gegensatz dazu Ihre andere Skripten, wie startup_script.sh
laufen , ohne die -e
Flagge:
sh /home/incero/startup_script.sh
Folglich werden sie auch dann ausgeführt, wenn ein Befehl einen Fehler meldet.
Das ist normal und gut. rc.local
sollte mit aufgerufen werden sh -e
und die meisten anderen Skripte - einschließlich der meisten von - rc.local
sollten nicht ausgeführt werden.
Denken Sie daran, den Unterschied zu beachten:
Skripts werden mit einem sh -e
Fehler beim Beenden der Berichterstellung ausgeführt, wenn ein Befehl, den sie enthalten, zum ersten Mal einen Fehler beim Beenden der Berichterstellung verursacht.
Es ist, als ob das Skript ein einzelner langer Befehl wäre, der aus allen Befehlen im Skript besteht, die mit &&
Operatoren verknüpft sind.
Skripte, die mit sh
(ohne -e
) ausgeführt werden, werden solange ausgeführt, bis sie zu einem Befehl gelangen, der sie beendet (beendet), oder bis zum Ende des Skripts. Der Erfolg oder Misserfolg jedes Befehls ist im Wesentlichen irrelevant (es sei denn, der nächste Befehl überprüft ihn). Das Skript wird mit dem Beendigungsstatus der letzten Befehlsausführung beendet.
Helfen Sie Ihrem Skript zu verstehen, dass es doch kein solcher Fehler ist
Wie können Sie verhindern, dass Ihr Skript als fehlgeschlagen eingestuft wird, wenn dies nicht der Fall ist?
Sie sehen, was passiert, bevor es fertig ist.
Wenn ein Befehl fehlgeschlagen ist, obwohl er erfolgreich sein sollte, finden Sie heraus, warum und beheben Sie das Problem.
Wenn ein Befehl fehlgeschlagen ist und dies richtig war, verhindern Sie, dass sich dieser Fehlerstatus ausbreitet.
Eine Möglichkeit, die Weitergabe eines Fehlerstatus zu verhindern, besteht darin, einen anderen erfolgreichen Befehl auszuführen. /bin/true
hat keine Nebenwirkungen und meldet Erfolg (genau wie /bin/false
nichts und auch scheitert).
Zum anderen muss sichergestellt werden, dass das Skript durch beendet wird exit 0
.
Das ist nicht unbedingt dasselbe wie exit 0
am Ende des Skripts. Beispielsweise könnte es einen if
-block geben, in dem das Skript beendet wird.
Es ist am besten zu wissen, was Ihr Skript dazu veranlasst, einen Fehler zu melden, bevor der Erfolg gemeldet wird. Wenn es auf irgendeine Weise wirklich versagt (in dem Sinne, dass Sie nicht das tun, was Sie wollen), möchten Sie nicht, dass es wirklich über den Erfolg berichtet.
Eine schnelle Lösung
Wenn Sie nicht machen können startup_script.sh
Ausgang Bericht Erfolg, können Sie den Befehl in ändern , rc.local
dass es läuft, so dass der Befehl Berichte Erfolg , auch wenn dies startup_script.sh
nicht tat.
Derzeit haben Sie:
sh /home/incero/startup_script.sh
Dieser Befehl hat die gleichen Nebenwirkungen (dh die Nebenwirkung des Laufens startup_script.sh
), aber immer meldet Erfolg:
sh /home/incero/startup_script.sh || /bin/true
Denken Sie daran, es ist besser , zu wissen , warum startup_script.sh
meldet eine Störung, und beheben.
So funktioniert die Schnellkorrektur
Dies ist eigentlich ein Beispiel für einen ||
Test, einen oder einen Test.
Angenommen, Sie fragen, ob ich den Müll herausgenommen oder die Eule gebürstet habe. Wenn ich den Müll nahm, kann ich ehrlich sagen „Ja“, auch wenn ich mich nicht erinnern, ob ich die Eule gebürstet.
Der Befehl links von wird ausgeführt ||
. Wenn es erfolgreich ist (true), muss die rechte Seite nicht ausgeführt werden. Wenn also ein startup_script.sh
Erfolg gemeldet wird, wird der true
Befehl nie ausgeführt.
Wenn jedoch ein startup_script.sh
Fehler gemeldet wird [ich habe den Müll nicht herausgenommen] , ist das Ergebnis von /bin/true
[wenn ich die Eule gebürstet habe] von Bedeutung .
/bin/true
Gibt immer Erfolg zurück (oder wahr , wie wir es manchmal nennen). Folglich ist der gesamte Befehl erfolgreich, und der nächste Befehl rc.local
kann ausgeführt werden.
Eine zusätzliche Anmerkung zu Erfolg / Misserfolg, wahr / falsch, null / ungleich null.
Fühlen Sie sich frei, dies zu ignorieren. Möglicherweise möchten Sie dies lesen, wenn Sie in mehr als einer Sprache programmieren (dh nicht nur mit Shell-Skripten).
Es ist ein Punkt großer Verwirrung für Shell-Skripter, die sich mit Programmiersprachen wie C befassen, und für C-Programmierer, die sich mit Shell-Skripten befassen, zu entdecken:
In Shell Scripting:
- Ein Rückgabewert von
0
bedeutet Erfolg und / oder wahr .
- Ein Rückgabewert von etwas anderem als
0
bedeutet Fehler und / oder Falsch .
In der C-Programmierung:
- Ein Rückgabewert von
0
bedeutet false .
- Ein Rückgabewert von etwas anderem als
0
Mittel wahr .
- Es gibt keine einfache Regel, was Erfolg und was Misserfolg bedeutet. Manchmal
0
bedeutet Erfolg, ein anderes Mal bedeutet es Misserfolg, ein anderes Mal bedeutet es, dass die Summe der beiden soeben hinzugefügten Zahlen Null ist. Rückgabewerte in der allgemeinen Programmierung werden verwendet, um eine Vielzahl verschiedener Arten von Informationen zu signalisieren.
- Der numerische Exit-Status eines Programms sollte gemäß den Regeln für Shell-Skripte Erfolg oder Misserfolg anzeigen. Das heißt, obwohl
0
dies in Ihrem C-Programm false bedeutet, geben Sie Ihr Programm trotzdem 0
als Exit-Code zurück, wenn Sie möchten, dass es erfolgreich war (was die Shell dann als true interpretiert ).