Pourquoi rc.local n'exécute-t-il pas toutes mes commandes et que puis-je y faire?

31

J'ai le script rc.local suivant:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

sh /home/incero/startup_script.sh
/bin/chmod +x /script.sh
sh /script.sh

exit 0

La première ligne, startup_script.sh , télécharge réellement le fichier script.sh dans /script.sh auquel il est fait référence à la troisième ligne.

Malheureusement, il semble que le script ne soit pas exécutable ou qu’il ne lance pas le script. L'exécution manuelle du fichier rc.local après avoir démarré fonctionne parfaitement. Est-ce que chmod ne peut pas être lancé au démarrage ou autre?

    
posée Programster 10.01.2013 - 17:01
la source

3 réponses

65

Vous pouvez passer à Un correctif rapide mais ce n’est pas forcément la meilleure option. Je vous recommande donc de lire tout cela en premier.

rc.local ne tolère pas les erreurs.

rc.local ne permet pas de récupérer intelligemment des erreurs. Si une commande échoue, elle cesse de fonctionner. La première ligne, #!/bin/sh -e , est exécutée dans un shell appelé avec l'indicateur -e . L'indicateur -e est ce qui fait qu'un script (dans ce cas, rc.local ) arrête de s'exécuter la première fois qu'une commande échoue.

Vous voulez que rc.local se comporte comme ça. Si une commande échoue, vous refusez de continuer avec les autres commandes de démarrage susceptibles de s’appuyer sur elle.

Donc, si une commande échoue, les commandes suivantes ne seront pas exécutées. Le problème ici est que /script.sh ne s'est pas exécuté (pas qu'il a échoué, voir ci-dessous), donc probablement une commande avant son échec. Mais lequel?

Était-ce /bin/chmod +x /script.sh ?

Non.

chmod fonctionne bien à tout moment. Si le système de fichiers contenant /bin a été monté, vous pouvez exécuter /bin/chmod . Et /bin est monté avant que rc.local s'exécute.

Lorsqu'il est exécuté en tant que root, /bin/chmod échoue rarement. Il échouera si le fichier sur lequel il fonctionne est en lecture seule et que pourrait échouer si le système de fichiers sur lequel il se trouve ne prend pas en charge les autorisations. Ni est probable ici.

Au fait, sh -e est la seule raison seulement pour laquelle cela poserait effectivement problème si chmod échouait. Lorsque vous exécutez un fichier de script en appelant explicitement son interpréteur, le fait que le fichier soit marqué comme exécutable importe peu. Ce n'est que si elle a déclaré /script.sh que le bit exécutable du fichier est important. Puisqu'il dit sh /script.sh , il ne le fait pas (à moins bien sûr que /script.sh s'appelle lui-même pendant son exécution, ce qui pourrait ne pas être exécutable, mais il est peu probable qu'il s'appelle lui-même). p>

Qu'est-ce qui a échoué?

sh /home/incero/startup_script.sh a échoué. Presque certainement.

Nous savons que cela a fonctionné, car il a téléchargé /script.sh .

(Sinon, il serait important de s’assurer qu’il a bien fonctionné, au cas où /bin n’était pas dans PATH - rc.local n'a pas nécessairement le même PATH que lorsque vous êtes connecté. Si /bin n'était pas dans le chemin de rc.local , cela exige que sh soit exécuté en tant que /bin/sh . Comme il a été exécuté, /bin est dans PATH , ce qui signifie que vous pouvez exécuter d'autres commandes situées dans /bin , sans qualifier complètement leurs noms. vous ne pouvez exécuter que chmod plutôt que /bin/chmod . Cependant, conformément à votre style dans rc.local , j'ai utilisé des noms complets pour toutes les commandes sauf sh , chaque fois que je vous suggère de les exécuter. / sup>

Nous pouvons être certains que /bin/chmod +x /script.sh n'a jamais couru (ou que /script.sh a été exécuté). Et nous savons que sh /script.sh n’a pas été utilisé non plus.

Mais il a téléchargé /script.sh . Ça a réussi! Comment pourrait-il échouer?

Deux sens du succès

Il y a deux choses différentes qu'une personne peut vouloir dire quand il / elle dit qu'une commande a réussi:

  1. Il a fait ce que vous vouliez qu’il fasse.
  2. Il a signalé que cela avait réussi.

Et il en va de même pour l’échec. Lorsqu'une personne déclare qu'une commande a échoué, cela peut signifier:

  1. Il n'a pas fait ce que vous vouliez qu'il fasse.
  2. Il a signalé que cela avait échoué.

Un script exécuté avec sh -e , comme rc.local , cessera de s'exécuter la première fois qu'une commande signale un échec . Cela ne fait pas de différence ce que la commande a réellement fait.

Sauf si vous avez l'intention que startup_script.sh signale un échec lorsqu'il fait ce que vous voulez, c'est un bogue dans startup_script.sh .

  • Certains bogues empêchent un script de faire ce que vous voulez. Ils affectent ce que les programmeurs appellent ses effets secondaires .
  • Certains bogues empêchent un script de générer des rapports correctement, qu’il ait réussi ou non. Ils affectent ce que les programmeurs appellent sa valeur de retour (qui dans ce cas est un exit status ).

Il est plus que probable que startup_script.sh a fait tout ce qui était nécessaire, sauf a signalé qu’il avait échoué.

Comment le succès ou l’échec est signalé

Un script est une liste de zéro ou plusieurs commandes. Chaque commande a un statut de sortie. En supposant qu'il n'y ait pas de défaillance dans l'exécution du script (par exemple, si l'interpréteur n'a pas pu lire la ligne suivante du script lors de son exécution), le statut de sortie d'un script est le suivant:

  • 0 (succès) si le script était vide (c'est-à-dire sans commandes).
  • N , si le script se termine par la commande exit N , où N correspond à un code de sortie.
  • Le code de sortie de la dernière commande exécutée dans le script, sinon.

Lorsqu'un exécutable s'exécute, il affiche son propre code de sortie - il ne s'agit pas uniquement de scripts.(Et techniquement, les codes de sortie des scripts sont les codes de sortie renvoyés par les shells qui les exécutent.)

Par exemple, si un programme C se termine par exit(0); ou return 0; dans sa fonction main() , le code 0 est attribué au système d'exploitation, ce qui lui donne accès au processus appelant (qui peut, pour Par exemple, soyez le shell à partir duquel le programme a été exécuté).

0 signifie que le programme a réussi. Tout autre nombre signifie qu'il a échoué. (De cette façon, différents numéros peuvent parfois renvoyer à différentes raisons pour lesquelles le programme a échoué.)

Les commandes doivent échouer

Parfois, vous exécutez un programme avec l’intention d’échouer. Dans ces situations, vous pourriez penser que son échec est un succès, même si ce n'est pas un bogue que le programme signale un échec. Par exemple, vous pouvez utiliser rm sur un fichier que vous suspectez déjà inexistant, juste pour vous assurer qu'il est supprimé.

Quelque chose comme cela se produit probablement dans startup_script.sh , juste avant qu’il ne s’exécute. La dernière commande à exécuter dans le script signale probablement un échec (même si son "échec" est peut-être totalement inexact ou même nécessaire), ce qui rend le script défaillant.

Tests censés échouer

Un type particulier de commande est un test , ce qui signifie une commande exécutée pour sa valeur de retour plutôt que ses effets secondaires. En d'autres termes, un test est une commande qui est exécutée de sorte que son statut de sortie puisse être examiné (et appliqué).

Par exemple, supposons que j'oublie si 4 est égal à 5. Heureusement, je connais les scripts shell:

if [ 4 -eq 5 ]; then
    echo "Yeah, they're totally the same."
fi

Ici, le test [ -eq 5 ] échoue car il s'avère 4 out 5 après tout. Cela ne signifie pas que le test n'a pas fonctionné correctement; ça faisait. Le travail consistait à vérifier si 4 = 5, puis à signaler le succès si tel était le cas, et à échouer si ce n’est pas le cas.

Vous voyez, dans les scripts shell, le succès peut également signifier true , et l'échec peut également signifier false .

Même si l'instruction echo ne s'exécute jamais, le bloc if dans son ensemble renvoie le succès.

Cependant, je suppose que je l'ai écrit plus court:

[ 4 -eq 5 ] && echo "Yeah, they're totally the same."

C'est un raccourci courant. && est un opérateur booléen et . Une expression && , constituée de && avec des instructions de chaque côté, renvoie false (échec) sauf si les deux côtés renvoient true (succès). Juste comme un et normal.

Si quelqu'un vous demande, "est-ce que Derek est allé au centre commercial et a pensé à un papillon?" et vous savez que Derek n'est pas allé au centre commercial, vous n'avez pas à vous soucier de savoir s'il pensait à un papillon.

De même, si la commande à gauche de && échoue (false), l’ensemble de l’expression && échoue immédiatement (false). L'instruction à droite de && n'est jamais exécutée.

Ici, [ 4 -eq 5 ] s'exécute. Cela "échoue" (retourne faux). Donc, toute l'expression && échoue. echo "Yeah, they're totally the same." ne fonctionne jamais. Tout s'est comporté comme il se doit, mais cette commande indique failure (même si if conditionnel ci-dessus indique le succès).

Si c'était la dernière instruction d'un script (et que le script y arrivait au lieu de se terminer à un moment donné), le script entier signalerait failure .

Il y a beaucoup de tests en plus de cela. Par exemple, il existe des tests avec || ("ou"). Cependant, l'exemple ci-dessus devrait suffire à expliquer ce que sont les tests et vous permettre d'utiliser efficacement la documentation pour déterminer si une instruction / commande particulière est un test.

sh vs. sh -e , revisité

Depuis la ligne #! (voir aussi cette question ) en haut de /etc/rc.local a sh -e , le système d'exploitation exécute le script comme s'il avait été appelé avec le commande:

sh -e /etc/rc.local

En revanche, vos autres scripts, tels que startup_script.sh , exécutent sans l’indicateur -e :

sh /home/incero/startup_script.sh

Par conséquent, ils continuent à fonctionner même lorsqu'une commande signale un échec.

C'est normal et bon. rc.local devrait être appelé avec sh -e et la plupart des autres scripts, y compris la plupart des scripts exécutés par rc.local , ne devraient pas.

Assurez-vous simplement de vous souvenir de la différence:

  • Les scripts exécutés avec sh -e exit signalent l’échec lors de la première exécution d’une commande contenant un message de fin de rapport.

    C'est comme si le script était une longue commande unique composée de toutes les commandes du script jointes aux opérateurs && .

  • Les scripts exécutés avec sh (sans -e ) continuent à fonctionner jusqu'à ce qu'ils atteignent une commande qui les termine (ou les quitte), ou jusqu'à la fin du script. Le succès ou l'échec de chaque commande est essentiellement sans importance (à moins que la commande suivante ne le vérifie). Le script se ferme avec le statut de sortie de la dernière commande exécutée.

Aider votre script à comprendre que ce n’est pas un échec après tout

Comment pouvez-vous empêcher votre script de penser qu’il a échoué alors que ce n’était pas le cas?

Vous regardez ce qui se passe juste avant la fin de son exécution.

  1. Si une commande a échoué alors qu’elle aurait dû réussir, déterminez pourquoi et résolvez le problème.

  2. Si une commande a échoué et que c'était la bonne chose à faire, empêchez cet état d'échec de se propager.

    • L'une des méthodes permettant d'éviter la propagation d'un état d'échec consiste à exécuter une autre commande qui aboutit. /bin/true n'a aucun effet secondaire et rapporte le succès (tout comme /bin/false ne fait rien et échoue également).

    • Une autre consiste à s’assurer que le script est terminé par exit 0 .

      Ce n'est pas nécessairement la même chose que exit 0 étant à la fin du script. Par exemple, il peut y avoir un if -block où le script se trouve à l'intérieur.

Il est préférable de savoir ce qui fait que votre script signale un échec avant de le rendre efficace. Si vous échouez vraiment (dans le sens où vous ne voulez pas faire ce que vous voulez), vous ne voulez pas vraiment qu’elle rapporte le succès.

Un correctif rapide

Si vous ne parvenez pas à générer un rapport de sortie startup_script.sh , vous pouvez modifier la commande dans rc.local qui l'exécute afin que la commande indique le succès même si startup_script.sh ne l'a pas fait.

Actuellement, vous avez:

sh /home/incero/startup_script.sh

Cette commande a les mêmes effets secondaires (c.-à-d. l'effet secondaire de l'exécution de startup_script.sh ), mais rapporte toujours le succès:

sh /home/incero/startup_script.sh || /bin/true

Rappelez-vous qu'il est préférable de savoir pourquoi startup_script.sh signale un échec et de le corriger.

Comment fonctionne le correctif rapide

Ceci est en fait un exemple de test || , un test ou .

Supposons que vous demandiez si je sortais la poubelle ou si je brossais la chouette. Si je sors les ordures, je peux dire en toute honnêteté "oui", même si je ne me souviens pas si j'ai brossé ou non la chouette.

La commande à gauche de || s'exécute. Si elle réussit (true), le côté droit ne doit pas être exécuté. Ainsi, si startup_script.sh signale le succès, la commande true ne s'exécute jamais.

Cependant, si startup_script.sh signale un échec [je n'ai pas sorti la corbeille] , alors le résultat de /bin/true [si j'ai brossé le hibou] compte.

/bin/true renvoie toujours le succès (ou true , comme nous l'appelons parfois). Par conséquent, la commande entière réussit et la commande suivante dans rc.local peut être exécutée.

Une note supplémentaire sur succès / échec, vrai / faux, zéro / non nul.

N'hésitez pas à l'ignorer. Vous voudrez peut-être lire ceci si vous programmez dans plusieurs langues (c’est-à-dire, pas seulement les scripts shell).

C'est un point de grande confusion pour les scripteurs shell qui utilisent des langages de programmation comme C, et pour les programmeurs en C qui utilisent les scripts shell, pour découvrir:

  • Dans les scripts shell:

    1. Une valeur de retour de 0 signifie succès et / ou true .
    2. Une valeur renvoyée par autre chose que 0 signifie échec et / ou false .
  • En programmation C:

    1. Une valeur de retour de 0 signifie false ..
    2. Une valeur renvoyée par autre chose que 0 signifie true .
    3. Il n’ya pas de règle simple pour savoir ce que signifie le succès et ce qui signifie l’échec. Parfois, 0 signifie le succès, d'autres fois l'échec, d'autres la somme des deux nombres que vous venez d'ajouter est zéro. Les valeurs de retour dans la programmation générale sont utilisées pour signaler une grande variété de différents types d'informations.
    4. Le statut de sortie numérique d'un programme doit indiquer le succès ou l'échec conformément aux règles du script shell. En d’autres termes, même si 0 signifie faux dans votre programme C, vous faites quand même en sorte que votre programme retourne 0 comme code de sortie si vous voulez qu’il le signale (que le shell interprète alors comme true ).
réponse donnée Eliah Kagan 14.01.2013 - 14:04
la source
1

Vous pouvez (dans les variantes Unix que j'utilise) remplacer le comportement par défaut en modifiant:

#!/bin/sh -e

Pour:

#!/bin/sh

Au début du script. Le drapeau "-e" demande à sh de sortir sur la première erreur. Le nom long de ce drapeau est "errexit", donc la ligne originale est équivalente à:

#!/bin/sh --errexit
    
réponse donnée Bryce 04.12.2015 - 22:09
la source
-4

changer la première ligne de: #!/bin/sh -e à !/bin/sh -e

    
réponse donnée elJenso 14.01.2013 - 10:22
la source

Lire d'autres questions sur les étiquettes