Puede saltar hasta A Quick Fix, pero esa no es necesariamente la mejor opción. Así que recomiendo leer todo esto primero.
rc.local
No tolera errores.
rc.local
no proporciona una forma inteligente de recuperarse de los errores. Si algún comando falla, deja de ejecutarse. La primera línea, #!/bin/sh -e
hace que se ejecute en un shell invocado con la -e
bandera. La -e
bandera es lo que hace que un script (en este caso rc.local
) deje de ejecutarse la primera vez que un comando falla dentro de él.
Quieres rc.local
comportarte así. Si un comando falla, no desea que continúe con cualquier otro comando de inicio que pueda confiar en que haya tenido éxito.
Entonces, si algún comando falla, los siguientes comandos no se ejecutarán. El problema aquí es que /script.sh
no se ejecutó (no es que haya fallado, ver más abajo), por lo que lo más probable es que haya algún comando antes de fallar. ¿Pero cual?
Fue /bin/chmod +x /script.sh
?
No.
chmod
funciona bien en cualquier momento. Siempre que /bin
se haya montado el sistema de archivos que contiene , puede ejecutarlo /bin/chmod
. Y /bin
se monta antes de las rc.local
carreras.
Cuando se ejecuta como root, /bin/chmod
rara vez falla. Fallará si el archivo en el que opera es de solo lectura, y podría fallar si el sistema de archivos en el que se encuentra no admite permisos. Tampoco es probable aquí.
Por cierto, sh -e
es la única razón por la que realmente sería un problema si chmod
fallara. Cuando ejecuta un archivo de script invocando explícitamente a su intérprete, no importa si el archivo está marcado como ejecutable. Solo si decía /script.sh
que importaría el bit ejecutable del archivo. Como dice sh /script.sh
, no lo hace (a menos que, por supuesto, se /script.sh
llame a sí mismo mientras se ejecuta, lo que podría fallar si no es ejecutable, pero es poco probable que se llame a sí mismo).
Entonces, ¿qué falló?
sh /home/incero/startup_script.sh
ha fallado. Casi seguro
Sabemos que se ejecutó, porque se descargó /script.sh
.
(De lo contrario, sería importante asegurarse de que se ejecutara, en caso de que de alguna manera /bin
no estuviera en RUTA , rc.local
no necesariamente tiene lo mismo PATH
que usted tenía cuando inició sesión. Si /bin
no estaba en rc.local
el camino, esto requeriría sh
ejecutarse como /bin/sh
. Como se ejecutó, /bin
está en PATH
, lo que significa que puede ejecutar otros comandos que se encuentran en /bin
, sin calificar completamente sus nombres. Por ejemplo, puede ejecutar solo en chmod
lugar de /bin/chmod
. Sin embargo, de acuerdo con su estilo en rc.local
, he usado nombres completos para todos los comandos sh
, excepto cuando sugiero que los ejecute).
Podemos estar bastante seguros de que /bin/chmod +x /script.sh
nunca se ejecutó (o vería que /script.sh
se ejecutó). Y sabemos que sh /script.sh
tampoco se ejecutó.
Pero se descargó /script.sh
. ¡Lo logró! ¿Cómo podría fallar?
Dos significados del éxito
Hay dos cosas diferentes que una persona puede querer decir cuando dice que un comando tuvo éxito:
- Hizo lo que querías que hiciera.
- Informó que tuvo éxito.
Y así es por el fracaso. Cuando una persona dice que un comando falló, podría significar:
- No hizo lo que querías que hiciera.
- Informó que falló.
Un script ejecutado con sh -e
, como rc.local
, dejará de ejecutarse la primera vez que un comando informa que falló . No importa lo que realmente hizo el comando.
A menos que tenga la intención de startup_script.sh
informar un error cuando hace lo que quiere, esto es un error startup_script.sh
.
- Algunos errores impiden que un script haga lo que usted quiere que haga. Afectan lo que los programadores llaman sus efectos secundarios .
- Y algunos errores evitan que un script informe correctamente si tuvo éxito o no. Afectan lo que los programadores llaman su valor de retorno (que en este caso es un estado de salida ).
Es muy probable que haya startup_script.sh
hecho todo lo que debería, excepto que informó que falló.
Cómo se informa el éxito o el fracaso
Un script es una lista de cero o más comandos. Cada comando tiene un estado de salida. Suponiendo que no haya fallas en la ejecución real del script (por ejemplo, si el intérprete no pudo leer la siguiente línea del script mientras lo ejecuta), el estado de salida de un script es:
0
(éxito) si el script estaba en blanco (es decir, no tenía comandos).
N
, si el script finalizó como resultado del comando , ¿dónde hay algún código de salida?exit N
N
- El código de salida del último comando que se ejecutó en el script, de lo contrario.
Cuando se ejecuta un ejecutable, informa su propio código de salida: no son solo para scripts. (Y técnicamente, los códigos de salida de los scripts son los códigos de salida devueltos por los shells que los ejecutan).
Por ejemplo, si un programa C termina con exit(0);
, o return 0;
en su main()
función, el código 0
se entrega al sistema operativo, que lo proporciona al proceso de llamada (que puede ser, por ejemplo, el shell desde el que se ejecutó el programa).
0
significa que el programa tuvo éxito. Cualquier otro número significa que falló. (De esta forma, diferentes números a veces pueden referirse a diferentes razones por las que el programa falló).
Comandos destinados a fallar
A veces, ejecuta un programa con la intención de que falle. En estas situaciones, puede pensar que su falla es exitosa, aunque no es un error que el programa informe una falla. Por ejemplo, puede usar rm
un archivo que sospecha que ya no existe, solo para asegurarse de que se elimine.
Probablemente ocurra algo como esto startup_script.sh
, justo antes de que deje de funcionar. El último comando que se ejecuta en el script probablemente informa un error (aunque su "error" podría ser totalmente correcto o incluso necesario), lo que hace que el script informe un error.
Pruebas destinadas a fallar
Un tipo especial de comando es una prueba , con lo que quiero decir que un comando se ejecuta por su valor de retorno en lugar de sus efectos secundarios. Es decir, una prueba es un comando que se ejecuta para poder examinar su estado de salida (y actuar sobre él).
Por ejemplo, supongamos que olvido si 4 es igual a 5. Afortunadamente, conozco los scripts de shell:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Aquí, la prueba [ -eq 5 ]
falla porque resulta 4 ≠ 5 después de todo. Eso no significa que la prueba no se realizó correctamente; lo hizo. Su trabajo consistía en verificar si 4 = 5, luego informar el éxito si es así y el fracaso si no.
Verá, en los scripts de shell, el éxito también puede significar verdadero , y el fracaso también puede significar falso .
Aunque la echo
declaración nunca se ejecuta, el if
bloque en su conjunto devuelve el éxito.
Sin embargo, supuse que lo había escrito más corto:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Esto es una taquigrafía común. &&
es un booleano y operador. Una &&
expresión, que consiste &&
en declaraciones a ambos lados, devuelve falso (falla) a menos que ambos lados devuelvan verdadero (éxito). Al igual que un normal y .
Si alguien te pregunta, "¿Derek fue al centro comercial y pensó en una mariposa?" y sabes que Derek no fue al centro comercial, no tienes que molestarte en averiguar si pensó en una mariposa.
Del mismo modo, si el comando a la izquierda de &&
falla (falso), la &&
expresión completa falla inmediatamente (falso). La declaración en el lado derecho de &&
nunca se ejecuta.
Aquí [ 4 -eq 5 ]
corre. "Falla" (devuelve falso). Entonces toda la &&
expresión falla. echo "Yeah, they're totally the same."
nunca corre. Todo se comportó como debería ser, pero este comando informa de un error (aunque el if
condicional equivalente por encima de lo anterior informa de éxito).
Si esa fuera la última declaración en un guión (y el guión llegó a ella, en lugar de terminar en algún momento antes), todo el guión informaría un fallo .
Hay muchas pruebas además de esto. Por ejemplo, hay pruebas con ||
("o"). Sin embargo, el ejemplo anterior debería ser suficiente para explicar qué son las pruebas y permitirle utilizar de manera efectiva la documentación para determinar si una instrucción / comando en particular es una prueba.
sh
vs. sh -e
, revisitado
Desde la #!
línea (vea también esta pregunta ) en la parte superior de /etc/rc.local
has sh -e
, el sistema operativo ejecuta el script como si fuera invocado con el comando:
sh -e /etc/rc.local
Por el contrario, sus otros scripts, como startup_script.sh
, se ejecutan sin la -e
bandera:
sh /home/incero/startup_script.sh
En consecuencia, siguen ejecutándose incluso cuando un comando en ellos informa de un error.
Esto es normal y bueno. rc.local
debe invocarse con sh -e
y la mayoría de los otros scripts, incluida la mayoría de los scripts ejecutados por, rc.local
no debe.
Solo asegúrate de recordar la diferencia:
Las secuencias de comandos se ejecutan con un sh -e
error de informe de salida la primera vez que un comando que contienen sale informando un error.
Es como si el script fuera un único comando largo que consta de todos los comandos del script unidos a &&
operadores.
Las secuencias de comandos que se ejecutan con sh
(sin -e
) continúan ejecutándose hasta que llegan a un comando que termina (sale de ellas) o hasta el final de la secuencia de comandos. El éxito o el fracaso de cada comando es esencialmente irrelevante (a menos que el siguiente comando lo verifique). El script sale con el estado de salida del último comando ejecutado.
Ayudar a su guión a comprender que no es un fracaso después de todo
¿Cómo puedes evitar que tu guión piense que falló cuando no lo hizo?
Miras lo que sucede justo antes de que termine de ejecutarse.
Si un comando falla cuando debería haber tenido éxito, descubra por qué y solucione el problema.
Si un comando falla, y eso fue lo correcto, entonces evite que el estado de falla se propague.
Una forma de evitar que se propague un estado de falla es ejecutar otro comando que tenga éxito. /bin/true
no tiene efectos secundarios e informa de éxito (al igual /bin/false
que no hace nada y también falla).
Otra es asegurarse de que el script finalice exit 0
.
Eso no es necesariamente lo mismo que exit 0
estar al final del guión. Por ejemplo, puede haber un if
bloque donde el script sale adentro.
Es mejor saber qué causa que su secuencia de comandos informe fallas, antes de hacer que informe éxito. Si realmente está fallando de alguna manera (en el sentido de no hacer lo que quieres que haga), realmente no quieres que informe de éxito.
Una solución rápida
Si no puede hacer que el startup_script.sh
informe de salida sea exitoso, puede cambiar el comando rc.local
que lo ejecuta, de modo que ese comando informa el éxito aunque startup_script.sh
no lo hizo.
Actualmente tienes:
sh /home/incero/startup_script.sh
Este comando tiene los mismos efectos secundarios (es decir, el efecto secundario de la ejecución startup_script.sh
), pero siempre informa el éxito:
sh /home/incero/startup_script.sh || /bin/true
Recuerde, es mejor saber por qué startup_script.sh
informa un error y solucionarlo.
Cómo funciona la solución rápida
Este es en realidad un ejemplo de una ||
prueba, una o prueba.
Supongamos que preguntas si saqué la basura o rocé la lechuza. Si saqué la basura, puedo decir sinceramente "sí", incluso si no recuerdo si rocé o no la lechuza.
El comando a la izquierda de ||
carreras. Si tiene éxito (verdadero), entonces el lado derecho no tiene que correr. Entonces, si startup_script.sh
informa éxito, el true
comando nunca se ejecuta.
Sin embargo, si startup_script.sh
informa un fallo [no saqué la basura] , entonces el resultado de /bin/true
[si cepillé la lechuza] es importante.
/bin/true
siempre devuelve el éxito (o verdadero , como a veces lo llamamos). En consecuencia, todo el comando tiene éxito y rc.local
puede ejecutarse el siguiente comando .
Una nota adicional sobre éxito / fracaso, verdadero / falso, cero / distinto de cero.
Siéntase libre de ignorar esto. Sin embargo, es posible que desee leer esto si programa en más de un idioma (es decir, no solo scripts de shell).
Es un punto de gran confusión para los scripters de shell que utilizan lenguajes de programación como C, y para los programadores de C que utilizan scripts de shell, para descubrir:
En scripting de shell:
- Un valor de retorno de
0
significa éxito y / o verdadero .
- Un valor de retorno de algo distinto de
0
significa falla y / o falso .
En programación en C:
- Un valor de retorno de
0
significa falso .
- Un valor de retorno de algo distinto de
0
medios verdaderos .
- No existe una regla simple para lo que significa éxito y lo que significa fracaso. A veces
0
significa éxito, otras veces significa fracaso, otras veces significa que la suma de los dos números que acaba de sumar es cero. Los valores de retorno en la programación general se utilizan para señalar una amplia variedad de diferentes tipos de información.
- El estado de salida numérica de un programa debe indicar el éxito o el fracaso de acuerdo con las reglas para la secuencia de comandos de shell. Es decir, aunque
0
significa falso en su programa C, aún hace que su programa regrese 0
como su código de salida si desea que informe que tuvo éxito (que el shell luego interpreta como verdadero ).
incero
(supongo que ese es el nombre de usuario) es un administrador de todos modos , y por lo tanto se le permite ejecutar cualquier comando comoroot
(al ingresar su propia contraseña de usuario), que no es súper inseguro y terrible . Si desea otras soluciones, le recomiendo publicar una nueva pregunta sobre esto . Un problema fue por qué los comandosrc.local
no se ejecutaron (y también verificó qué pasaría si los hiciera continuar ejecutándose). Ahora tiene un problema diferente: desea conectar la máquina a la red antes derc.local
ejecutarla o programar la ejecuciónstartup_script.sh
posterior./usr/bin/sudo service networking restart
al script de inicio antes del comando wget resultó en wget y luego funcionó.rc.local
Puede (en las variantes de Unix que uso) anular el comportamiento predeterminado cambiando:
A:
Al comienzo del guión. La bandera "-e" indica a sh que salga en el primer error. El nombre largo de esa bandera es "errexit", por lo que la línea original es equivalente a:
fuente
-e
trabajos en raspbian jessie.-e
está ahí por una razón. No haría eso si fuera tú. Pero no soy tus padres.cambiar la primera línea de:
#!/bin/sh -e
a!/bin/sh -e
fuente
#!
es la correcta. (Al menos, la#!
parte es correcta. Y el resto generalmente funciona bien para, pararc.local
). Vea este artículo y esta pregunta . De todos modos, ¡bienvenido a Ask Ubuntu!