Puoi saltare fino a Una correzione rapida ma questa non è necessariamente l'opzione migliore. Quindi raccomando di leggere tutto questo per primo.
rc.local
non tollera errori.
rc.local
non fornisce un modo per recuperare in modo intelligente dagli errori. Se un comando non riesce, smette di funzionare. La prima riga, #!/bin/sh -e
fa sì che venga eseguita in una shell invocata con il -e
flag. Il -e
flag è ciò che fa rc.local
fermare l'esecuzione di uno script (in questo caso ) la prima volta che un comando fallisce al suo interno.
Vuoi rc.local
comportarti così. Se un comando ha esito negativo, non si desidera che continui con qualsiasi altro comando di avvio potrebbe fare affidamento sul fatto che sia riuscito.
Quindi se un comando fallisce, i comandi successivi non verranno eseguiti. Il problema qui è che /script.sh
non è stato eseguito (non che non è riuscito, vedi sotto), quindi molto probabilmente qualche comando prima che fallisse. Ma quale?
È stato /bin/chmod +x /script.sh
?
No.
chmod
funziona bene in qualsiasi momento. A condizione che sia /bin
stato montato il filesystem che contiene , è possibile eseguire /bin/chmod
. Ed /bin
è montato prima delle rc.local
corse.
Quando eseguito come root, /bin/chmod
raramente fallisce. Non funzionerà se il file su cui opera è di sola lettura e potrebbe non funzionare se il file system su cui si trova non supporta le autorizzazioni. Nessuno dei due è probabilmente qui.
A proposito, sh -e
è l' unica ragione per cui in realtà sarebbe un problema se chmod
fallito. Quando si esegue un file di script invocando esplicitamente il suo interprete, non importa se il file è contrassegnato come eseguibile. Solo se dicesse /script.sh
il bit eseguibile del file è importante. Dal momento che dice sh /script.sh
, non lo fa (a meno che ovviamente non si /script.sh
chiami mentre è in esecuzione, il che potrebbe non riuscire a non essere eseguibile, ma è improbabile che si chiami da solo).
Quindi cosa è fallito?
sh /home/incero/startup_script.sh
fallito. Quasi certamente.
Sappiamo che è stato eseguito perché scaricato /script.sh
.
(Altrimenti, sarebbe importante assicurarsi che funzionasse, nel caso in cui in qualche modo /bin
non fosse nel PERCORSO - rc.local
non ha necessariamente la stessa PATH
cosa che hai quando sei loggato. Se /bin
non fosse nel rc.local
percorso, questo richiederebbe sh
di essere eseguito come /bin/sh
. Dato che è stato eseguito, /bin
è in PATH
, il che significa che è possibile eseguire altri comandi che si trovano in /bin
, senza qualificare completamente i loro nomi. Ad esempio, è possibile eseguire solo chmod
anziché /bin/chmod
. Tuttavia, in linea con il proprio stile in rc.local
, ho usato nomi completi per tutti i comandi tranne sh
, ogni volta che sto suggerendo di eseguirli.)
Possiamo essere abbastanza sicuri che /bin/chmod +x /script.sh
non sia mai stato eseguito (o vedresti che è /script.sh
stato eseguito). E sappiamo che sh /script.sh
non è stato eseguito neanche.
Ma è stato scaricato /script.sh
. È riuscito! Come potrebbe fallire?
Due significati di successo
Ci sono due cose diverse che una persona potrebbe significare quando dice che un comando ha avuto successo:
- Ha fatto quello che volevi che facesse.
- Ha riferito che ci è riuscito.
E così è per il fallimento. Quando una persona dice che un comando non è riuscito, potrebbe significare:
- Non ha fatto quello che volevi che facesse.
- Ha riferito di aver fallito.
Uno script eseguito sh -e
, ad esempio rc.local
, interromperà l'esecuzione la prima volta che un comando segnala che non è riuscito . Non fa differenza quello che ha effettivamente fatto il comando.
A meno che tu non intenda startup_script.sh
segnalare un errore quando fa quello che vuoi, questo è un bug startup_script.sh
.
- Alcuni bug impediscono a uno script di fare ciò che vuoi che faccia. Influiscono su ciò che i programmatori chiamano i suoi effetti collaterali .
- E alcuni bug impediscono a uno script di riportare correttamente se è riuscito o meno. Influiscono su ciò che i programmatori chiamano il suo valore di ritorno (che in questo caso è uno stato di uscita ).
È molto probabile che abbia startup_script.sh
fatto tutto il possibile, tranne per il fatto che ha fallito.
Come viene segnalato successo o fallimento
Uno script è un elenco di zero o più comandi. Ogni comando ha uno stato di uscita. Supponendo che non vi siano errori nell'esecuzione effettiva dello script (ad esempio, se l'interprete non è in grado di leggere la riga successiva dello script durante l'esecuzione), lo stato di uscita di uno script è:
0
(esito positivo) se lo script era vuoto (ovvero non aveva comandi).
N
, se lo script è terminato a seguito del comando , dove è presente un codice di uscita.exit N
N
- In caso contrario, il codice di uscita dell'ultimo comando eseguito nello script.
Quando viene eseguito un eseguibile, segnala il proprio codice di uscita - non sono solo per gli script. (E tecnicamente, i codici di uscita dagli script sono i codici di uscita restituiti dalle shell che li eseguono.)
Ad esempio, se un programma C termina con exit(0);
, o return 0;
nella sua main()
funzione, il codice 0
viene dato al sistema operativo, che lo fornisce al processo di chiamata (che può, ad esempio, essere la shell da cui è stato eseguito il programma).
0
significa che il programma è riuscito. Ogni altro numero indica che è fallito. (In questo modo, numeri diversi possono a volte fare riferimento a motivi diversi per cui il programma ha avuto esito negativo.)
Comandi destinati a fallire
A volte, si esegue un programma con l'intenzione che fallirà. In queste situazioni, potresti considerare il suo fallimento come un successo, anche se non è un bug che il programma riporti l'errore. Ad esempio, potresti utilizzare rm
un file che sospetti già non esista, solo per assicurarti che sia eliminato.
Qualcosa del genere sta probabilmente accadendo startup_script.sh
, appena prima che smetta di funzionare. L' ultimo comando da eseguire nello script sta probabilmente segnalando un errore (anche se il suo "fallimento" potrebbe essere del tutto soddisfacente o addirittura necessario), il che rende l'errore di riportare lo script.
Test destinati a fallire
Un tipo speciale di comando è un test , con il quale intendo un comando eseguito per il suo valore di ritorno piuttosto che per i suoi effetti collaterali. Cioè, un test è un comando che viene eseguito in modo che il suo stato di uscita possa essere esaminato (e seguito).
Ad esempio, supponiamo di dimenticare se 4 è uguale a 5. Fortunatamente, conosco lo scripting della shell:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Qui, il test [ -eq 5 ]
fallisce perché dopo tutto risulta 4 ≠ 5. Ciò non significa che il test non sia stato eseguito correttamente; lo ha fatto. Il suo compito era controllare se 4 = 5, quindi segnalare il successo in tal caso e il fallimento in caso contrario.
Vedete, nello scripting della shell, il successo può anche significare vero e il fallimento può anche significare falso .
Anche se l' echo
affermazione non viene mai eseguita, il if
blocco nel suo insieme restituisce successo.
Tuttavia, suppongo di averlo scritto più breve:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Questa è una scorciatoia comune. &&
è un booleano e un operatore. &&
Un'espressione, che consiste di &&
dichiarazioni su entrambi i lati, restituisce false (guasto) a meno che entrambi i lati restituiscono true (successo). Proprio come un normale e .
Se qualcuno ti chiede "Derek è andato al centro commerciale e ha pensato a una farfalla?" e sai che Derek non è andato al centro commerciale, non devi preoccuparti di capire se pensasse a una farfalla.
Allo stesso modo, se il comando a sinistra di &&
non riesce (falso), l'intera &&
espressione fallisce immediatamente (falso). L'affermazione sul lato destro di &&
non viene mai eseguita.
Ecco, [ 4 -eq 5 ]
corre. "Fallisce" (restituendo false). Quindi l'intera &&
espressione fallisce. echo "Yeah, they're totally the same."
non corre mai. Tutto si è comportato come dovrebbe essere, ma questo comando segnala un errore (anche se il if
condizionale altrimenti equivalente sopra riportato indica il successo).
Se quella fosse l'ultima affermazione in uno script (e lo script è arrivato ad esso, piuttosto che terminare ad un certo punto prima di esso), l'intero script segnalerebbe l' errore .
Ci sono molti test oltre a questo. Ad esempio, ci sono test con ||
("o"). Tuttavia, l'esempio sopra dovrebbe essere sufficiente per spiegare quali sono i test e consentire all'utente di utilizzare efficacemente la documentazione per determinare se un determinato comando / istruzione è un test.
sh
vs. sh -e
, rivisitato
Poiché la #!
riga (vedi anche questa domanda ) nella parte superiore di /etc/rc.local
has sh -e
, il sistema operativo esegue lo script come se fosse stato invocato con il comando:
sh -e /etc/rc.local
Al contrario, gli altri script, come ad esempio startup_script.sh
, eseguire senza la -e
bandiera:
sh /home/incero/startup_script.sh
Di conseguenza continuano a funzionare anche quando un comando in essi riporta errori.
Questo è normale e buono. rc.local
dovrebbe essere invocato con sh -e
e la maggior parte degli altri script - inclusa la maggior parte degli script eseguiti da rc.local
- non dovrebbe.
Assicurati di ricordare la differenza:
Gli script vengono eseguiti con sh -e
errore di segnalazione delle uscite la prima volta che un comando in esse contenuto esce da errori di segnalazione.
È come se lo script fosse un singolo comando lungo costituito da tutti i comandi nello script uniti con gli &&
operatori.
Gli script eseguiti con sh
(senza -e
) continuano l'esecuzione fino a quando non arrivano a un comando che li termina (esce da) o fino alla fine dello script. L'esito positivo o negativo di ogni comando è essenzialmente irrilevante (a meno che il comando successivo non lo controlli). Lo script esce con lo stato di uscita dell'ultima esecuzione del comando.
Aiutare il tuo script a capire che dopo tutto non è un tale fallimento
Come puoi evitare che la tua sceneggiatura pensi che sia fallita quando non lo ha fatto?
Guarda cosa succede poco prima che finisca di funzionare.
Se un comando ha avuto esito negativo quando avrebbe dovuto avere esito positivo, capire perché e risolvere il problema.
Se un comando non ha funzionato e questa è stata la cosa giusta da fare, impedire la propagazione dello stato di errore.
Un modo per impedire la propagazione di uno stato di errore consiste nell'eseguire un altro comando che abbia esito positivo. /bin/true
non ha effetti collaterali e segnala il successo (proprio come /bin/false
non fa nulla e fallisce anche).
Un altro è assicurarsi che lo script sia terminato da exit 0
.
Non è necessariamente la stessa cosa di exit 0
essere alla fine della sceneggiatura. Ad esempio, potrebbe esserci un if
blocco in cui lo script esce all'interno.
È meglio sapere cosa fa sì che il tuo script riporti errori, prima di farlo registrare. Se in qualche modo sta fallendo (nel senso di non fare quello che vuoi che faccia), non vuoi davvero riportare il successo.
Una soluzione rapida
Se non è possibile eseguire startup_script.sh
correttamente la segnalazione di uscita, è possibile modificare il comando rc.local
che lo esegue, quindi tale comando segnala il successo anche se startup_script.sh
non lo ha fatto.
Attualmente hai:
sh /home/incero/startup_script.sh
Questo comando ha gli stessi effetti collaterali (ovvero l'effetto collaterale della corsa startup_script.sh
), ma riporta sempre il successo:
sh /home/incero/startup_script.sh || /bin/true
Ricorda, è meglio sapere perché startup_script.sh
segnala l'errore e risolverlo.
Come funziona la correzione rapida
Questo è in realtà un esempio di ||
test, test o .
Supponiamo che tu mi chieda se ho portato fuori la spazzatura o spazzolato il gufo. Se avessi portato fuori la spazzatura, potrei davvero dire "sì", anche se non ricordo se ho spazzolato il gufo.
Il comando a sinistra delle ||
corse. Se riesce (vero), allora il lato destro non deve correre. Quindi, se startup_script.sh
segnala un esito positivo, il true
comando non viene mai eseguito.
Tuttavia, se viene startup_script.sh
segnalato un errore [Non ho eliminato il cestino] , il risultato di /bin/true
[se ho spazzolato il gufo] è importante.
/bin/true
restituisce sempre successo (o vero , come a volte lo chiamiamo). Di conseguenza, l'intero comando ha esito positivo e rc.local
può essere eseguito il comando successivo in .
Una nota aggiuntiva su successo / fallimento, vero / falso, zero / diverso da zero.
Sentiti libero di ignorare questo. Potresti voler leggere questo se programmi in più di una lingua, però (cioè non solo script di shell).
È un punto di grande confusione per gli script di shell che adottano linguaggi di programmazione come C, e per i programmatori C che utilizzano script di shell, per scoprire:
Nella shell scripting:
- Un valore di ritorno
0
significa successo e / o vero .
- Un valore di ritorno di qualcosa di diverso da
0
significa fallimento e / o falso .
In programmazione C:
- Un valore restituito
0
significa false ..
- Un valore di ritorno di qualcosa di diverso da
0
significa vero .
- Non esiste una regola semplice per cosa significhi successo e cosa significhi fallimento. A volte
0
significa successo, altre volte significa fallimento, altre volte significa che la somma dei due numeri appena aggiunti è zero. I valori di ritorno nella programmazione generale vengono utilizzati per segnalare un'ampia varietà di diversi tipi di informazioni.
- Lo stato di uscita numerico di un programma dovrebbe indicare l'esito positivo o negativo in conformità con le regole per gli script di shell. Cioè, anche se
0
significa falso nel tuo programma C, fai comunque in modo che il tuo programma ritorni 0
come suo codice di uscita se vuoi che riporti che è riuscito (che la shell poi interpreta come vero ).