Você pode pular todo o caminho até A Quick Fix, mas essa não é necessariamente a melhor opção. Então, eu recomendo ler tudo isso primeiro.
rc.local
não tolera erros.
rc.local
não fornece uma maneira de recuperar de forma inteligente os erros. Se algum comando falhar, ele será interrompido. A primeira linha #!/bin/sh -e
,, faz com que seja executada em um shell chamado com o -e
sinalizador. O -e
sinalizador é o que faz com que um script (nesse caso rc.local
) pare de ser executado na primeira vez que um comando falhar nele.
Você quer rc.local
se comportar assim. Se um comando falhar, você não deseja que ele continue com os outros comandos de inicialização que possam ter sido bem-sucedidos.
Portanto, se algum comando falhar, os comandos subsequentes não serão executados. O problema aqui é que /script.sh
não foi executado (não que tenha falhado, veja abaixo); portanto, provavelmente algum comando antes de falhar. Mas qual deles?
Foi /bin/chmod +x /script.sh
?
Não.
chmod
corre bem a qualquer momento. Desde que o sistema de arquivos que contém /bin
foi montado, você pode executar /bin/chmod
. E /bin
é montado antes da rc.local
execução.
Quando executado como root, /bin/chmod
raramente falha. Ele falhará se o arquivo em que opera for somente leitura e poderá falhar se o sistema de arquivos em que estiver ligado não tiver permissões suportadas. Nem é provável aqui.
A propósito, sh -e
é a única razão pela qual seria realmente um problema se chmod
falhasse. Quando você executa um arquivo de script chamando explicitamente seu intérprete, não importa se o arquivo está marcado como executável. Somente se disser /script.sh
que o bit executável do arquivo será importante. Desde que diz sh /script.sh
, não o faz (a não ser, é claro /script.sh
, que se chame enquanto é executado, o que poderia falhar por não ser executável, mas é improvável que se chame).
Então, o que falhou?
sh /home/incero/startup_script.sh
falhou. Quase certamente.
Sabemos que funcionou, porque baixou /script.sh
.
(Caso contrário, seria importante garantir que ele funcionasse, caso de alguma forma /bin
não estivesse no PATH - rc.local
não necessariamente tem o mesmo PATH
que você tem quando está conectado. Se /bin
não estiver no rc.local
caminho, isso exigiria sh
ser executado como /bin/sh
. Desde que executou, /bin
está dentro PATH
, o que significa que você pode executar outros comandos localizados /bin
, sem qualificar totalmente seus nomes. Por exemplo, você pode executar apenas em chmod
vez de /bin/chmod
. No entanto, de acordo com seu estilo em rc.local
, eu usei nomes totalmente qualificados para todos os comandos exceto sh
, sempre que eu estou sugerindo que você executá-los.)
Podemos ter certeza de que /bin/chmod +x /script.sh
nunca funcionou (ou você verá que /script.sh
foi executado). E sabemos que sh /script.sh
também não foi executado.
Mas ele baixou /script.sh
. Conseguiu! Como isso poderia falhar?
Dois significados do sucesso
Há duas coisas diferentes que uma pessoa pode querer dizer quando diz que um comando foi bem-sucedido:
- Ele fez o que você queria que ele fizesse.
- Informou que conseguiu.
E assim é por fracasso. Quando uma pessoa diz que um comando falhou, isso pode significar:
- Não fez o que você queria.
- Informou que falhou.
Um script executado com sh -e
, por exemplo rc.local
, para de executar na primeira vez que um comando relata que falhou . Não faz diferença o que o comando realmente fez.
A menos que você pretenda startup_script.sh
relatar falha quando ela faz o que deseja, isso é um bug startup_script.sh
.
- Alguns erros impedem que um script faça o que você deseja. Eles afetam o que os programadores chamam de efeitos colaterais .
- E alguns erros impedem que um script relate corretamente se foi ou não bem-sucedido. Eles afetam o que os programadores chamam de valor de retorno (que neste caso é um status de saída ).
É mais provável que tenha startup_script.sh
feito tudo o que deveria, exceto que relatou que falhou.
Como o sucesso ou o fracasso é relatado
Um script é uma lista de zero ou mais comandos. Cada comando tem um status de saída. Supondo que não haja falhas na execução do script (por exemplo, se o intérprete não puder ler a próxima linha do script durante a execução), o status de saída de um script será:
0
(sucesso) se o script estava em branco (ou seja, não tinha comandos).
N
, se o script foi finalizado como resultado do comando , onde está algum código de saída.exit N
N
- O código de saída do último comando executado no script, caso contrário.
Quando um executável é executado, ele relata seu próprio código de saída - eles não são apenas para scripts. (E tecnicamente, os códigos de saída dos scripts são os códigos de saída retornados pelos shells que os executam.)
Por exemplo, se um programa C termina com exit(0);
, ou return 0;
em sua main()
função, o código 0
é fornecido ao sistema operacional, que o fornece ao processo de chamada (que pode, por exemplo, ser o shell no qual o programa foi executado).
0
significa que o programa foi bem-sucedido. Qualquer outro número significa que falhou. (Dessa forma, números diferentes às vezes podem se referir a diferentes razões pelas quais o programa falhou.)
Comandos com falha
Às vezes, você executa um programa com a intenção de que ele falhe. Nessas situações, você pode pensar em sua falha como um sucesso, mesmo que não seja um bug que o programa reporte falha. Por exemplo, você pode usar rm
um arquivo que você suspeita que já não exista, apenas para garantir que ele seja excluído.
Algo como isso provavelmente está acontecendo startup_script.sh
, pouco antes de parar de funcionar. O último comando a ser executado no script provavelmente está relatando falha (mesmo que sua "falha" possa ser totalmente boa ou até necessária), o que faz com que o script relate falha.
Testes com falha
Um tipo especial de comando é um teste , com o qual quero dizer um comando executado por seu valor de retorno, e não por seus efeitos colaterais. Ou seja, um teste é um comando executado para que seu status de saída possa ser examinado (e adotado).
Por exemplo, suponha que eu esqueça se 4 é igual a 5. Felizmente, eu sei scripts de shell:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Aqui, o teste [ -eq 5 ]
falha porque, afinal, 4 × 5. Isso não significa que o teste não foi executado corretamente; fez. Seu trabalho era verificar se 4 = 5 e, em caso afirmativo, relatar o sucesso e, se não, o fracasso.
Veja, no script de shell, sucesso também pode significar verdadeiro e falha também pode significar falso .
Mesmo que a echo
declaração nunca seja executada, o if
bloco como um todo retorna com sucesso.
No entanto, eu deveria ter escrito mais curto:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Isso é uma abreviação comum. &&
é um operador e booleano . Uma expressão, que consiste em declarações de ambos os lados, retorna falso (falha), a menos que ambos os lados retornem verdadeiro (sucesso). Assim como um normal e .&&
&&
Se alguém lhe perguntar: "Derek foi ao shopping e pensou em uma borboleta?" e você sabe que Derek não foi ao shopping, não precisa se preocupar em descobrir se ele pensou em uma borboleta.
Da mesma forma, se o comando à esquerda de &&
falhar (falso), toda a &&
expressão falhará imediatamente (falso). A declaração no lado direito da &&
tela nunca é executada.
Aqui, [ 4 -eq 5 ]
corre. "Falha" (retornando falso). Então toda a &&
expressão falha. echo "Yeah, they're totally the same."
nunca corre. Tudo se comportou como deveria ser, mas esse comando relata falha (mesmo que o if
condicional equivalente acima descrito relate sucesso).
Se essa fosse a última declaração de um script (e o script chegasse a ela, em vez de terminar em algum momento antes dela), o script inteiro relataria falha .
Existem muitos testes além disso. Por exemplo, existem testes com ||
("ou"). No entanto, o exemplo acima deve ser suficiente para explicar o que são testes e possibilitar o uso eficaz da documentação para determinar se uma determinada instrução / comando é um teste.
sh
vs. sh -e
, Revisitado
Como a #!
linha (veja também esta pergunta ) na parte superior do /etc/rc.local
has sh -e
, o sistema operacional executa o script como se tivesse sido chamado com o comando:
sh -e /etc/rc.local
Por outro lado, seus outros scripts, como startup_script.sh
, são executados sem o -e
sinalizador:
sh /home/incero/startup_script.sh
Conseqüentemente, eles continuam sendo executados mesmo quando um comando neles relata falha.
Isso é normal e bom. rc.local
deve ser chamado sh -e
e a maioria dos outros scripts - incluindo a maioria dos scripts executados por rc.local
- não deveria .
Apenas lembre-se da diferença:
Os scripts são executados com sh -e
falha no relatório de saída na primeira vez em que um comando contém sai do relatório.
É como se o script fosse um único comando longo que consistisse em todos os comandos do script associados aos &&
operadores.
Os scripts executados com sh
(sem -e
) continuam em execução até chegarem a um comando que os encerra (sai fora) ou até o final do script. O sucesso ou falha de cada comando é essencialmente irrelevante (a menos que o próximo comando verifique isso). O script sai com o status de saída do último comando executado.
Ajudando seu script a entender que não é um fracasso, afinal
Como você pode impedir que seu script pense que falhou quando não o fez?
Você olha o que acontece logo antes de terminar a execução.
Se um comando falhou quando deveria ter sido bem-sucedido, descubra o motivo e corrija o problema.
Se um comando falhar, e isso foi a coisa certa a acontecer, impeça a propagação do status da falha.
Uma maneira de impedir a propagação de um status de falha é executar outro comando que tenha êxito. /bin/true
não tem efeitos colaterais e relata sucesso (da mesma forma /bin/false
que nada faz e também falha).
Outra é garantir que o script seja finalizado por exit 0
.
Isso não é necessariamente a mesma coisa que exit 0
estar no final do script. Por exemplo, pode haver um if
bloco onde o script sai por dentro.
É melhor saber o que faz com que seu script relate falha, antes de torná-lo bem-sucedido. Se realmente está falhando de alguma forma (no sentido de não fazer o que você quer que ele faça), você realmente não quer que ele relate sucesso.
Uma solução rápida
Se você não conseguir obter startup_script.sh
êxito no relatório de saída, poderá alterar o comando rc.local
que o executa, para que o comando relate sucesso, mesmo que startup_script.sh
não o tenha feito.
Atualmente você tem:
sh /home/incero/startup_script.sh
Este comando tem os mesmos efeitos colaterais (ou seja, o efeito colateral da execução startup_script.sh
), mas sempre informa o sucesso:
sh /home/incero/startup_script.sh || /bin/true
Lembre-se, é melhor saber por que os startup_script.sh
relatórios falham e corrigi-lo.
Como a correção rápida funciona
Este é realmente um exemplo de ||
teste, um ou teste.
Suponha que você pergunte se eu tirei o lixo ou escovei a coruja. Se eu joguei o lixo fora, posso dizer "sim", mesmo que não me lembre se escovei ou não a coruja.
O comando à esquerda das ||
execuções. Se for bem-sucedido (verdadeiro), o lado direito não precisará ser executado. Portanto, se startup_script.sh
relatar êxito, o true
comando nunca será executado.
No entanto, se startup_script.sh
relatar falha [eu não joguei o lixo fora] , o resultado de /bin/true
[se eu escovei a coruja] será importante.
/bin/true
sempre retorna sucesso (ou verdadeiro , como às vezes chamamos). Conseqüentemente, todo o comando é bem-sucedido e o próximo comando rc.local
pode ser executado.
Uma observação adicional sobre sucesso / falha, verdadeiro / falso, zero / diferente de zero.
Sinta-se livre para ignorar isso. Você pode querer ler isso se programar em mais de um idioma (no entanto, não apenas scripts de shell).
É um ponto de grande confusão para os scripts de shell que adotam linguagens de programação como C e para os programadores em C que usam scripts de shell para descobrir:
No script de shell:
- Um valor de retorno
0
significa sucesso e / ou verdadeiro .
- Um valor de retorno de algo diferente de
0
significa falha e / ou falso .
Na programação C:
- Um valor de retorno
0
significa false .
- Um valor de retorno de algo que não
0
seja verdadeiro .
- Não existe uma regra simples para o que significa sucesso e o que significa fracasso. Às vezes
0
significa sucesso, outras vezes significa falha, outras vezes significa que a soma dos dois números que você acabou de adicionar é zero. Os valores de retorno na programação geral são usados para sinalizar uma ampla variedade de diferentes tipos de informações.
- O status de saída numérica de um programa deve indicar sucesso ou falha de acordo com as regras para scripts de shell. Ou seja, mesmo que isso
0
signifique falso no seu programa C, você ainda fará com que o programa retorne 0
como seu código de saída, se desejar que ele relate que foi bem-sucedido (que o shell interpreta como verdadeiro ).