Pro*_*ter 36 startup scripts execute-command
我有以下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
Run Code Online (Sandbox Code Playgroud)
第一行,startup_script.sh
实际下载第三行中提到的script.sh
文件/script.sh
。
不幸的是,它似乎没有使脚本可执行或运行脚本。rc.local
启动后手动运行文件效果很好。chmod 不能在启动时运行吗?
Eli*_*gan 72
您可以一直跳到快速修复,但这不一定是最佳选择。所以我建议先阅读所有这些。
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
不在PATH 中rc.local
,则此将需要sh
作为 运行/bin/sh
。因为它确实运行了,/bin
所以在 中PATH
,这意味着您可以运行位于 中的其他命令/bin
,而无需完全限定它们的名称。例如,您可以只运行chmod
而不是/bin/chmod
。但是,与您的风格保持一致在 中rc.local
,我对所有命令都使用了完全限定名称,除了sh
,每当我建议您运行它们时。)
我们可以肯定/bin/chmod +x /script.sh
从未运行过(或者你会看到它/script.sh
被执行了)。我们知道sh /script.sh
也没有运行。
但它下载了/script.sh
。它成功了!怎么会失败?
当一个人说命令成功时,他/她可能意味着两种不同的意思:
失败也是如此。当一个人说命令失败时,这可能意味着:
使用sh -e
, like运行的脚本rc.local
将在第一次报告失败的命令时停止运行。命令实际执行的操作没有区别。
除非您打算startup_script.sh
在执行您想要的操作时报告失败,否则这是startup_script.sh
.
最有可能的是,它startup_script.sh
做了它应该做的一切,除了报告它失败了。
脚本是零个或多个命令的列表。每个命令都有一个退出状态。假设实际运行脚本没有失败(例如,如果解释器在运行时无法读取脚本的下一行),则脚本的退出状态为:
0
(成功)如果脚本是空白的(即没有命令)。N
, 如果脚本因命令而结束,则某些退出代码在哪里。exit N
N
当一个可执行文件运行时,它会报告它自己的退出代码——它们不仅仅用于脚本。(从技术上讲,脚本的退出代码是运行它们的 shell 返回的退出代码。)
例如,如果一个 C 程序以exit(0);
或return 0;
在其main()
函数中结束,则代码0
将提供给操作系统,操作系统将其提供给调用进程(例如,它可能是运行程序的外壳程序)。
0
表示程序成功。每隔一个数字就意味着它失败了。(这样,不同的数字有时可以表示程序失败的不同原因。)
有时,您运行程序的意图是它会失败。在这些情况下,您可能会将其失败视为成功,即使程序报告失败并不是错误。例如,您可能会rm
在您怀疑已经不存在的文件上使用,只是为了确保它已被删除。
startup_script.sh
就在它停止运行之前,可能正在发生这样的事情。在脚本中运行的最后一个命令可能是报告失败(即使它的“失败”可能完全没有问题,甚至是必要的),这使得脚本报告失败。
一种特殊的命令是test,我的意思是一个命令是为了它的返回值而不是它的副作用而运行的。也就是说,测试是一个运行的命令,以便可以检查(并对其采取行动)其退出状态。
例如,假设我忘记了 4 是否等于 5。幸运的是,我知道 shell 脚本:
if [ 4 -eq 5 ]; then
echo "Yeah, they're totally the same."
fi
Run Code Online (Sandbox Code Playgroud)
在这里,测试[ -eq 5 ]
失败,因为结果是 4 ? 5 毕竟。这并不意味着测试没有正确执行;它做了。它的工作是检查是否 4 = 5,如果是,则报告成功,如果不是,则报告失败。
你看,在 shell 脚本中,成功也可能意味着true,失败也意味着false。
即使echo
语句从不运行,if
块作为一个整体确实返回成功。
但是,假设我写得更短:
[ 4 -eq 5 ] && echo "Yeah, they're totally the same."
Run Code Online (Sandbox Code Playgroud)
这是常见的速记。&&
是一个布尔值 和运算符。一个&&
表达式,它由&&
两边有陈述,返回false(失败),除非双方返回true(成功)。就像一个正常的和。
如果有人问你,“德里克有没有去商场想过一只蝴蝶?” 而且你知道德里克没有去商场,你不必费心弄清楚他是否想到了一只蝴蝶。
同样,如果左边的命令&&
失败 (false),则整个&&
表达式立即失败 (false)。右侧的语句&&
永远不会运行。
来,[ 4 -eq 5 ]
跑。它“失败”(返回false)。所以整个&&
表达式失败了。echo "Yeah, they're totally the same."
从不运行。一切都按其应有的方式运行,但此命令报告失败(即使if
上面的其他等效条件报告成功)。
如果这是脚本中的最后一条语句(并且脚本到达它,而不是在它之前的某个点终止),则整个脚本将报告failure。
除此之外还有很多测试。例如,有使用||
(“或”)的测试。但是,上面的示例应该足以解释什么是测试,并使您可以有效地使用文档来确定特定语句/命令是否是测试。
sh
对比sh -e
,重温由于has顶部的#!
行(另请参阅此问题),操作系统运行脚本就像它是用命令调用的一样:/etc/rc.local
sh -e
sh -e /etc/rc.local
Run Code Online (Sandbox Code Playgroud)
相比之下,其它脚本,例如startup_script.sh
,运行无该-e
标志:
sh /home/incero/startup_script.sh
Run Code Online (Sandbox Code Playgroud)
因此,即使其中的命令报告失败,它们也会继续运行。
这很正常也很好。rc.local
应该sh -e
与大多数其他脚本一起调用- 包括由 -- 运行的大多数脚本rc.local
不应该。
只要确保记住区别:
脚本sh -e
在第一次包含退出报告失败的命令时运行并报告失败。
就好像脚本是一个单一的长命令,由脚本中的所有命令和&&
运算符组成。
使用sh
(不使用-e
)运行的脚本会继续运行,直到到达终止(退出)它们的命令,或者到达脚本的最后。每个命令的成功或失败本质上是无关的(除非下一个命令检查它)。脚本以上次命令运行的退出状态退出。
你怎么能不让你的脚本在它没有失败的时候认为它失败了?
你看看在它运行完成之前会发生什么。
如果命令在本应成功时失败,请找出原因并解决问题。
如果命令失败,并且这是正确的事情发生,则防止该失败状态传播。
防止失败状态传播的一种方法是运行另一个成功的命令。/bin/true
没有副作用并报告成功(就像/bin/false
什么都不做也失败一样)。
另一个是确保脚本以exit 0
.
这不一定与exit 0
脚本末尾相同。例如,可能有一个if
-block 脚本在里面退出。
在让它报告成功之前,最好先了解是什么导致您的脚本报告失败。如果它确实以某种方式失败了(在没有做你想让它做的意义上),你真的不希望它报告成功。
如果您不能使startup_script.sh
退出报告成功,您可以更改rc.local
运行它的命令,以便该命令即使startup_script.sh
没有成功也报告成功。
目前你有:
sh /home/incero/startup_script.sh
Run Code Online (Sandbox Code Playgroud)
这个命令有同样的副作用(即运行的副作用startup_script.sh
),但总是报告成功:
sh /home/incero/startup_script.sh || /bin/true
Run Code Online (Sandbox Code Playgroud)
请记住,最好知道为什么startup_script.sh
报告失败并修复它。
这实际上是一个||
测试、一个或测试的例子。
假设你问我是倒垃圾还是刷猫头鹰。如果我把垃圾倒掉了,即使我不记得我有没有刷过猫头鹰,我也可以如实说“是”。
||
运行左侧的命令。如果成功(真),则右侧不必运行。因此,如果startup_script.sh
报告成功,则该true
命令永远不会运行。
但是,如果startup_script.sh
报告失败【我没有倒垃圾】,那么/bin/true
【如果我刷猫头鹰】的结果很重要。
/bin/true
总是返回成功(或true,我们有时称之为)。因此,整个命令成功,并且rc.local
可以运行下一个命令。
随意忽略这一点。但是,如果您使用一种以上的语言编程(即,不仅仅是 shell 脚本),您可能想要阅读本文。
对于使用 C 等编程语言的 shell 脚本编写者和使用 shell 脚本编写的 C 程序员来说,发现:
在 shell 脚本中:
0
mean成功和/或true。0
意味着失败和/或false。在 C 编程中:
0
意味着false ..0
意味着true。0
意味着成功,有时意味着失败,有时意味着您刚刚添加的两个数字之和为零。一般编程中的返回值用于表示各种不同类型的信息。0
在您的 C 程序中意味着 false,0
如果您希望它报告它成功(然后 shell 将其解释为true),您仍然可以让您的程序作为其退出代码返回。