您可以一直跳到“快速修复”,但这不一定是最佳选择。因此,我建议先阅读所有这些内容。
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 N
N
- 否则,脚本中运行的最后一个命令的退出代码。
当可执行文件运行时,它将报告自己的退出代码-它们不仅用于脚本。(从技术上讲,脚本的退出代码是运行它们的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
上述等效条件报告成功)。
如果那是脚本中的最后一条语句(脚本到达了该语句,而不是终止于它之前的某个点),则整个脚本将报告失败。
除此之外,还有很多测试。例如,存在带有||
(“或”)的测试。但是,上面的示例足以说明什么是测试,并使您可以有效地使用文档来确定特定的语句/命令是否为测试。
sh
vs. sh -e
,再访
由于has 顶部的#!
行(另请参阅此问题),操作系统将运行脚本,就像使用以下命令调用该脚本一样:/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
退出报告失败时第一次运行,其中包含的命令退出报告失败。
好像脚本是单个长命令,由脚本中的所有命令与&&
运算符组成。
以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)。