您可以一直跳到“快速修复”,但这不一定是最佳选择。因此,我建议先阅读所有这些内容。
rc.local 不容忍错误。
rc.local没有提供一种从错误中智能恢复的方法。如果任何命令失败,它将停止运行。第一行#!/bin/sh -e导致它在用-e标志调用的shell中执行。该-e标志是使脚本(在本例中为rc.local)第一次在脚本中失败时停止运行的原因。
您想rc.local表现得像这样。如果命令失败,则您不希望其继续执行任何其他启动命令,而要依赖它。
因此,如果任何命令失败,则后续命令将不会运行。这里的问题是/script.sh没有运行(不是失败,请参见下文),因此很有可能在失败之前执行了某些命令。但是哪一个呢?
是/bin/chmod +x /script.sh吗
没有。
chmod随时运行正常。如果包含/bin已挂载的文件系统,则可以运行/bin/chmod。并/bin在rc.local运行前安装。
以root身份运行时,/bin/chmod很少会失败。它会失败,如果它的运作上的文件是只读的,并且可能如果它是在文件系统中不支持权限失败。这里都不可能。
顺便说一句,这是如果失败实际上会成为问题sh -e的唯一原因chmod。通过显式调用脚本解释器运行脚本文件时,该文件是否标记为可执行文件并不重要。只有它说/script.sh了文件的可执行位才重要。既然它说了sh /script.sh,它就不会(除非它在运行时自动/script.sh 调用它,否则可能由于无法执行而失败,但是它不太可能调用它自己)。
那么什么失败了?
sh /home/incero/startup_script.sh失败了 几乎肯定。
我们知道它已运行,因为它已下载/script.sh。
(否则,确保它确实可以运行很重要,以防万一某种/bin原因不在PATH中 - rc.local不一定与PATH登录时的相同。如果/bin不在rc.local的路径中,这将需要sh以as身份运行/bin/sh。由于它确实/bin在in中运行,PATH这意味着您可以运行位于中的其他命令/bin,而无需完全限定其名称。例如,您可以运行chmod而不是/bin/chmod。在中rc.local,sh无论何时建议运行它们,我都对所有命令使用完全限定的名称。)
我们可以确定/bin/chmod +x /script.sh永远不会运行(或者您会看到它/script.sh已执行)。而且我们知道sh /script.sh也没有运行。
但是下载了/script.sh。成功了!它怎么会失败?
成功的两个意义
当他/她说命令成功时,人可能要表达两种不同的意思:
- 它完成了您想要的操作。
- 它报告说成功了。
所以这是为了失败。当某人说命令失败时,可能意味着:
- 它没有执行您想要的操作。
- 它报告说失败了。
与运行的脚本sh -e,比如rc.local,将停止运行,在第一时间命令报告说,它失败了。该命令实际执行的操作没有任何区别。
除非您打算在startup_script.sh失败时报告失败,否则这是中的错误startup_script.sh。
- 一些错误会阻止脚本执行您想要的操作。它们影响程序员所谓的副作用。
- 而且某些错误会阻止脚本正确报告是否成功。它们会影响程序员称呼它的返回值(在这种情况下为退出状态)。
除了报告失败外,最有可能startup_script.sh做了所有应做的事情。
如何报告成功或失败
脚本是零个或多个命令的列表。每个命令都有退出状态。假设实际运行脚本没有任何失败(例如,如果解释器在运行脚本时无法读取脚本的下一行),则脚本的退出状态为:
0 (成功)如果脚本为空(即没有命令)。
N,如果脚本是由于命令而结束的,则是一些退出代码。exit NN
- 否则,脚本中运行的最后一个命令的退出代码。
当可执行文件运行时,它将报告自己的退出代码-它们不仅用于脚本。(从技术上讲,脚本的退出代码是运行它们的shell返回的退出代码。)
例如,如果C程序以exit(0);或return 0;在其main()功能结尾,则将代码0提供给操作系统,该操作系统将其提供给调用进程(例如,可以是运行程序的shell)。
0表示程序成功。其他所有数字均表示失败。(这样,不同的数字有时可以表示程序失败的不同原因。)
命令意味着失败
有时,您在运行程序时会失败。在这种情况下,即使程序报告失败不是错误,您也可以将其视为成功。例如,您可以rm在怀疑尚不存在的文件上使用,以确保已将其删除。
startup_script.sh停止运行之前,可能会在中发生这样的事情。在脚本中运行的最后一条命令可能是报告失败(即使其“失败”可能完全正常甚至是必要的),这会使脚本报告失败。
测试意味着失败
一种特殊的命令是test,它的意思是命令针对返回值而不是副作用运行。也就是说,测试是运行的命令,因此可以检查其退出状态(并对其执行操作)。
例如,假设我忘了4是否等于5。幸运的是,我知道shell脚本:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
在这里,测试[ -eq 5 ]失败了,因为毕竟结果为4≠5。这并不意味着测试不能正确执行。它做了。工作是检查4 = 5,然后报告成功,如果失败,则报告失败。
您会看到,在shell脚本中,成功也可能意味着true,而失败也可能意味着false。
即使该echo语句从不运行,该if块作为一个整体也确实会返回成功。
但是,假设我将其写得更短:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
这是常见的简写。&&是一个布尔值 和运算符。一个&&表达式,它由&&两边有陈述,返回false(失败),除非双方返回true(成功)。就像一个正常的和。
如果有人问你,“德里克(Derek)到商场去想蝴蝶了吗?” 而且您知道Derek没去过商场,您不必费心弄清楚他是否想到了蝴蝶。
同样,如果左边的命令&&失败(false),则整个&&表达式立即失败(false)。右侧的语句&&永远不会运行。
在这里,[ 4 -eq 5 ]跑步。它“失败”(返回假)。因此整个&&表达式失败。echo "Yeah, they're totally the same."永远不会运行。一切都按其应有的方式运行,但是此命令报告失败(即使if上述等效条件报告成功)。
如果那是脚本中的最后一条语句(脚本到达了该语句,而不是终止于它之前的某个点),则整个脚本将报告失败。
除此之外,还有很多测试。例如,存在带有||(“或”)的测试。但是,上面的示例足以说明什么是测试,并使您可以有效地使用文档来确定特定的语句/命令是否为测试。
shvs. sh -e,再访
由于has 顶部的#!行(另请参阅此问题),操作系统将运行脚本,就像使用以下命令调用该脚本一样:/etc/rc.localsh -e
sh -e /etc/rc.local
相比之下,其它脚本,例如startup_script.sh,运行无该-e标志:
sh /home/incero/startup_script.sh
因此,即使其中的命令报告失败,它们也可以继续运行。
这是正常的,很好的。rc.local应该与sh -e其他大多数脚本(包括大多数由-运行的脚本)一起调用rc.local。
只要确保记住不同之处:
脚本在sh -e退出报告失败时第一次运行,其中包含的命令退出报告失败。
好像脚本是单个长命令,由脚本中的所有命令与&&运算符组成。
以sh(无-e)运行的脚本会继续运行,直到到达终止(退出)它们的命令或脚本的末尾为止。每个命令的成功或失败基本上是无关紧要的(除非下一个命令对其进行检查)。脚本以上次运行命令的退出状态退出。
帮助您的脚本理解这毕竟不是失败
您如何避免脚本失败而又没有失败呢?
您看一下即将运行完成时发生的情况。
如果命令应该成功执行失败,请找出原因并解决问题。
如果命令失败了,那是正确的事情,那么请阻止该失败状态传播。
在使脚本报告成功之前,最好知道是什么导致脚本报告失败。如果它确实在某种程度上失败了(从某种意义上说,它没有做您想要做的事情),那么您就不希望它报告成功。
快速修复
如果无法使startup_script.sh出口报告成功,则可以更改rc.local运行该命令的命令,这样即使startup_script.sh没有成功,该命令也可以报告成功。
目前您有:
sh /home/incero/startup_script.sh
此命令具有相同的副作用(即running的副作用startup_script.sh),但始终报告成功:
sh /home/incero/startup_script.sh || /bin/true
请记住,最好知道为什么会startup_script.sh报告故障并进行修复。
快速修复的工作方式
这实际上是一个||测试或一个测试的示例。
假设您问我是拿出垃圾桶还是刷了猫头鹰。如果我拿出垃圾桶,即使我不记得是否刷过猫头鹰,也可以如实地说“是”。
||运行左侧的命令。如果成功(true),则无需运行右侧。因此,如果startup_script.sh报告成功,该true命令将永远不会运行。
但是,如果startup_script.sh报告失败[我没有清除垃圾],则/bin/true [如果我刷了猫头鹰]的结果很重要。
/bin/true总是返回成功(或有时称为true)。因此,整个命令成功执行,并且下一个命令rc.local可以运行。
关于成功/失败,正确/错误,零/非零的附加说明。
随意忽略这一点。但是,如果您使用一种以上的语言进行编程,则可能需要阅读本说明(即,不仅是shell脚本编写)。
对于使用C之类的编程语言的Shell脚本编写者和使用Shell脚本的C程序员而言,发现以下内容非常令人困惑:
在shell脚本中:
- 返回值
0表示成功和/或true。
- 返回值以外的其他值
0表示失败和/或false。
在C编程中:
- 返回值
0意味着false ..
- 比其他东西的返回值
0的手段真。
- 对于什么意味着成功和什么意味着失败,没有简单的规则。有时
0表示成功,有时表示失败,有时表示您刚添加的两个数字的总和为零。一般编程中的返回值用于表示各种不同类型的信息。
- 程序的数字退出状态应根据Shell脚本编写规则指示成功或失败。也就是说,即使
0在您的C程序中表示false ,但是0如果您希望它报告它已成功执行,则仍将程序作为其退出代码返回(然后,shell将其解释为true)。