次のrc.local
スクリプトがあります。
#!/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
最初の行startup_script.sh
は、実際にscript.sh
ファイルを3行目で参照される/script.sh
にダウンロードします。
残念ながら、スクリプトを実行可能にしたり、スクリプトを実行したりしていないようです。 rc.local
ファイルを開始した後に手動で実行すると、完全に機能します。起動時などにchmodを実行できませんか?
A Quick Fixまでずっとスキップできますが、必ずしも最良のオプションではありません。だから、最初にこのすべてを読むことをお勧めする。
rc.local
はエラーを許容しません。rc.local
は、エラーからインテリジェントに回復する方法を提供しません。コマンドが失敗すると、実行が停止します。最初の行#!/bin/sh -e
により、-e
フラグで呼び出されたシェルで実行されます。 -e
フラグは、スクリプト(この場合、rc.local
)の中でコマンドが最初に失敗したときにスクリプトの実行を停止させるものです。
rc.local
がこのように動作するようにします。コマンドが失敗した場合は、notを実行し、他のスタートアップコマンドが成功したことに依存している可能性のあるものを続行します。
そのため、いずれかのコマンドが失敗すると、後続のコマンドは実行されません。ここでの問題は、/script.sh
が実行されなかったことです(失敗したことではなく、以下を参照)。しかし、どれですか?
/bin/chmod +x /script.sh
でしたか?番号。
chmod
はいつでも正常に動作します。 /bin
を含むファイルシステムがマウントされていれば、/bin/chmod
を実行できます。 /bin
は、rc.local
が実行される前にマウントされます。
Rootとして実行すると、/bin/chmod
はほとんど失敗しません。操作対象のファイルが読み取り専用の場合は失敗し、そのファイルシステムが権限をサポートしていない場合はmightが失敗します。どちらもここにはありません。
ところで、sh -e
はonlyの理由で、chmod
が失敗した場合に実際に問題になります。インタープリターを明示的に呼び出してスクリプトファイルを実行する場合、そのファイルが実行可能とマークされているかどうかは関係ありません。 /script.sh
と言われた場合にのみ、ファイルの実行可能ビットが問題になります。 sh /script.sh
と書かれているので、(もちろん/script.sh
は実行中にそれ自体を呼び出しません)それ自体を呼び出すことはほとんどありません)。
sh /home/incero/startup_script.sh
が失敗しました。ほぼ間違いなく。
/script.sh
をダウンロードしたため、実行されたことがわかります。
(それ以外の場合は、何らかの理由で/bin
が PATH --rc.local
に同じPATH
が含まれていない場合に実行することを確認することが重要です。 /bin
がrc.local
のパスにない場合、/bin/sh
としてsh
を実行する必要があります。実行後、/bin
はPATH
にあるため、名前を完全修飾せずに/bin
にある他のコマンドを実行できます。たとえば、/bin/chmod
ではなくchmod
だけを実行できます。 rc.local
のスタイルでは、実行を提案するときは常に、sh
を除くすべてのコマンドに完全修飾名を使用しています。)
/bin/chmod +x /script.sh
が実行されたことはないと確信できます(または、/script.sh
が実行されたことがわかります)。そして、sh /script.sh
も実行されなかったことがわかります。
しかし、/script.sh
をダウンロードしました。成功しました!どうして失敗するのでしょうか?
コマンドが成功したと言ったときに人が意味する可能性のある2つの異なることがあります。
そしてそれは失敗のためです。人がコマンドが失敗したと言うとき、それは以下を意味する可能性があります。
sh -e
のようなrc.local
で実行されるスクリプトは、コマンドが失敗したことを最初に報告したときに実行を停止します。コマンドが実際に行ったことに違いはありません。
startup_script.sh
が望むことをするときに失敗を報告するつもりでない限り、これはstartup_script.sh
のバグです。
startup_script.sh
がすべきことをすべて実行した可能性が最も高く、exceptは失敗したと報告しました。
スクリプトは、ゼロ個以上のコマンドのリストです。各コマンドには終了ステータスがあります。スクリプトの実際の実行に失敗がないと仮定すると(たとえば、インタープリターが実行中にスクリプトの次の行を読み取れなかった場合)、スクリプトの終了ステータスは次のとおりです。
0
(成功)スクリプトが空白の場合(つまり、コマンドがなかった場合)。N
、コマンドexit N
の結果としてスクリプトが終了した場合、ここでN
は終了コードです。実行可能ファイルは、実行されると、それ自身の終了コードを報告します。スクリプト用だけではありません。 (技術的には、スクリプトからの終了コードは、スクリプトを実行するシェルによって返される終了コードです。)
たとえば、Cプログラムがそのexit(0);
関数でmain()
またはreturn 0;
で終わる場合、コード0
がオペレーティングシステムに与えられ、呼び出しプロセスに提供されます。 (これは、たとえば、プログラムが実行されたシェルである可能性があります)。
0
は、プログラムが成功したことを意味します。 1つおきの数字は失敗したことを意味します。 (このように、異なる番号は、プログラムが失敗したさまざまな理由を指す場合があります。)
場合によっては、失敗することを意図してプログラムを実行します。これらの状況では、プログラムが失敗を報告するのはバグではありませんが、失敗は成功と考えるかもしれません。たとえば、削除されていることを確認するためだけに、すでに存在しないと思われるファイルに対してrm
を使用できます。
おそらく、実行が停止する直前に、startup_script.sh
でこのようなことが起こっています。スクリプトでを実行する最後のコマンドが失敗を報告している可能性があります(その「失敗」はまったく問題がないか、または必要な場合でも)。
特別な種類のコマンドの1つはa testです。これは、副作用ではなく戻り値に対してコマンドを実行することを意味します。つまり、テストとは、終了ステータスを調査(および実行)できるように実行されるコマンドです。
たとえば、4が5に等しいかどうかを忘れるとします。幸いなことに、私はシェルスクリプトを知っています。
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
ここで、テスト[ -eq 5 ]
は、結局4≠5であるため失敗します。それは、テストが正しく実行されなかったことを意味しません。やった4 = 5かどうかを確認し、そうであれば成功を報告し、そうでなければ失敗を報告するのが仕事でした。
シェルスクリプトでは、成功はtrueを意味し、失敗はfalseを意味することもあります。
echo
ステートメントは実行されませんが、if
ブロックは全体として成功を返します。
しかし、私はそれを短く書いたと仮定します:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
これは一般的な略記法です。 &&
は booleanand演算子です。両側にステートメントがある&&
で構成される&&
式は、両側がtrue(成功)を返さない限りfalse(失敗)を返します。通常のandと同じです。
「デレクはモールに行って蝶のことを考えた?」と尋ねられたら。デレクがモールに行かなかったことを知っているなら、彼が蝶のことを考えているかどうかを気にする必要はありません。
同様に、&&
の左側のコマンドが失敗した場合(false)、&&
式全体がすぐに失敗します(false)。 &&
の右側のステートメントは実行されません。
ここでは、[ 4 -eq 5 ]
が実行されます。 「失敗」(falseを返す)。したがって、&&
式全体が失敗します。 echo "Yeah, they're totally the same."
は実行されません。すべてが正常に動作しましたが、このコマンドはfailureを報告します(上記の条件付きの同等のif
条件付きは成功を報告しますが)。
それがスクリプトの最後のステートメントである場合(およびスクリプトは、その前のあるポイントで終了するのではなく、それに到達した場合)、スクリプト全体がfailureを報告します。
これ以外にも多くのテストがあります。たとえば、||
( "or")を使用したテストがあります。ただし、上記の例はテストとは何かを説明するのに十分であり、ドキュメントを効果的に使用して特定のステートメント/コマンドがテストかどうかを判断できるようにする必要があります。
sh
vs. sh -e
、再訪#!
行 ( この質問 も参照)/etc/rc.local
の先頭にsh -e
があるため、オペレーティングシステムはスクリプトを実行しますコマンドで呼び出されたかのように:
sh -e /etc/rc.local
対照的に、startup_script.sh
などの他のスクリプトは、-e
フラグなしでを実行します。
sh /home/incero/startup_script.sh
その結果、それらのコマンドが失敗を報告しても、それらは実行し続けます。
これは正常で良いことです。 rc.local
はsh -e
および他のほとんどのスクリプト(rc.local
によって実行されるほとんどのスクリプトを含む)で呼び出す必要があります。
違いを覚えておいてください:
スクリプトは、sh -e
exitで失敗を報告し、コマンドに含まれるコマンドが最初に失敗を報告したときに実行します。
スクリプトは、&&
演算子で結合されたスクリプト内のすべてのコマンドで構成される単一の長いコマンドであるかのようです。
スクリプトは、sh
(-e
なし)で実行され、スクリプトを終了する(終了する)コマンドに到達するまで、またはスクリプトの最後まで実行され続けます。すべてのコマンドの成功または失敗は、本質的に無関係です(次のコマンドでチェックされない限り)。スクリプトは、最後に実行されたコマンドの終了ステータスで終了します。
失敗したのに失敗したとスクリプトが考えないようにする方法を教えてください。
実行が完了する直前に何が起こるかを確認します。
成功するはずのコマンドが失敗した場合は、その理由を見つけて問題を修正します。
コマンドが失敗し、それが正しいことだった場合、その失敗ステータスの伝播を防ぎます。
失敗ステータスが伝播しないようにする1つの方法は、成功する別のコマンドを実行することです。 /bin/true
には副作用がなく、成功を報告します(/bin/false
は何もせず、失敗もします)。
もう1つは、exit 0
によってスクリプトが終了することを確認することです。
スクリプトの最後にあるexit 0
と必ずしも同じではありません。たとえば、スクリプトが内部で終了するif
- blockが存在する場合があります。
成功を報告する前に、スクリプトが失敗を報告する原因を知ることが最善です。それが本当に何らかの方法で失敗している場合(あなたがやりたいことをしていないという意味で)、あなたは本当にそれが成功を報告することを望まない。
startup_script.sh
exitで成功を報告できない場合は、rc.local
が実行しなかったにもかかわらずコマンドが成功を報告するように、実行するstartup_script.sh
のコマンドを変更できます。
現在、次のものがあります。
sh /home/incero/startup_script.sh
このコマンドには同じ副作用(つまり、startup_script.sh
を実行する副作用)がありますが、常に成功を報告します。
sh /home/incero/startup_script.sh || /bin/true
startup_script.sh
が失敗を報告する理由を知って、それを修正することを覚えておいてください。
これは実際には||
テスト、orテストの例です。
あなたが私がゴミを取り出したのか、フクロウを磨いたのか尋ねるとします。ゴミを取り出したら、フクロウを磨いたかどうか覚えていなくても、「はい」と正直に言うことができます。
||
の左側のコマンドが実行されます。成功した場合(true)、右側を実行する必要はありません。したがって、startup_script.sh
が成功を報告した場合、true
コマンドは実行されません。
ただし、startup_script.sh
が失敗を報告した場合[ゴミ箱を取り出さなかった]、その後/bin/true
の結果[フクロウを磨いたら]が重要です。
/bin/true
は常に成功を返します(またはtrue、時々呼び出します)。その結果、コマンド全体が成功し、rc.local
の次のコマンドを実行できます。
これを無視してください。ただし、複数の言語でプログラムする場合は、これを読むことをお勧めします(つまり、シェルスクリプトだけでなく)。
Cのようなプログラミング言語を使用するシェルスクリプト作成者にとって、またシェルスクリプトを使用するCプログラマーにとって、以下を発見することは大きな混乱のポイントです。
シェルスクリプト:
0
の戻り値は、successおよび/またはtrueを意味します。0
以外の戻り値は、failureおよび/またはfalseを意味します。Cプログラミングの場合:
0
の戻り値は、falseを意味します。0
以外の戻り値は、trueを意味します。0
は成功を意味する場合もあれば、失敗を意味する場合もあれば、追加したばかりの2つの数値の合計がゼロであることを意味する場合もあります。一般的なプログラミングの戻り値は、さまざまな種類の情報を通知するために使用されます。0
はfalseを意味しますが、成功したことを報告したい場合は、プログラムが0
を終了コードとして返すようにします(シェルはtrue)。以下を変更することで、デフォルトの動作をオーバーライドできます(私が使用するUnixバリアントでは)。
#!/bin/sh -e
に:
#!/bin/sh
スクリプトの開始時。 「-e」フラグは、shに最初のエラーで終了するよう指示します。そのフラグの長い名前は「errexit」であるため、元の行は次と同等です。
#!/bin/sh --errexit