Linux 开机启动脚本的坑

后续:一年后的新体会

开机软件自启请尽可能用 systemdrc.local不是万能的方案!有关 systemd 的教程看这里

引入

开机自启是方便操tou作lan的有效途径。在 Linux 中,典型的开机自启途径就是将命令添加进 rc.local 文件中。然而在修改后可能会有和预期不一致的现象,这在不同的 Linux 发行版上的原因还往往不同。这次我结合自己的经历,谈一下我遇到的坑和解决方案。

不算是坑的注意事项

执行权限

百度一下类似“rc.local 不执行”的关键字,基本每篇解决办法都会提到这一点。确实,脚本要有执行权限这是必要的前提。

1
$ sudo chmod +x rc.local

在第一次修改脚本后,确保权限设置好是一个好习惯。

#!规定的解释器

这也是能百度到的常见教程。Linux 下的 Shell 有很多种类,它们之间的语法会略有差别,目前来看最常用的还是 bash,其兼容性也不错。所以把文件头设置成 #!/bin/bash 说不定能解决一些奇怪的问题。

CentOS 下的坑

文件路径

在 CentOS 下,往往 /etc/rc.local/etc/rc.d/rc.local 的拷贝。所以修改时请用后者的路径,并加好执行权限。

环境变量引入

有时候可能一些命令无法执行的原因是执行过早,环境变量没完全引入。可以考虑在脚本执行语句的开头添加 sleep 5等待一下,这个时间可以看机器的实际情况酌情修改。

Ubuntu 下的坑

Systemctl 相关

至少从 Ubuntu 16 后,新引入的 systemctl 将执行 rc.local 当作开机启动的服务来处理。有时候这个服务不会自动地正确添加,我们可以手动完成。

  1. 将系统自带的自启服务模板拷贝出来,并修改

    1
    2
    $ sudo cp /lib/systemd/system/rc-local.service /etc/systemd/system/
    $ sudo vi /etc/systemd/system/rc-local.service
  2. 在文件默认添加下列内容

    1
    2
    [Install] 
    WantedBy=multi-user.target
  3. 开启服务

    1
    $ sudo systemctl enable rc-local.service

环境变量引入

我发现 Ubuntu 的启动脚本可能不会自动引入 /etc/profile 里的环境变量,所以最好把其写进启动脚本防止意外。(我的 Tomcat 就是因为这个无法自启)在 rc.local 脚本开头添加如下命令即可:

1
source /etc/profile

Debian 下的方法

之所以没说是坑,是因为其自启本身没什么问题,但是默认是隐藏的。只要存在可执行的 /etc/rc.local 文件,名为 rc-local.service 的服务就会启动。

Arch Linux 下默认不提供

没错,rc-local 是默认没有的。而且 aur 源里的也不太好用。确实从规范角度说,rc.local 并不是那么必要,System Daemon 已经足够好用了。不过对于我等爱偷懒人士,写一行启动命令比写一个服务要简单多了。于是我们的解决方案是把 Debian 上的 rc-local.service 移植过来。

创建服务

将如下内容写进 /lib/systemd/system/rc-local.service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target

创建 rc.local 文件,并赋予权限:

1
2
$ sudo touch /etc/rc.local
$ sudo chmod +x /etc/rc.local

编辑启动脚本,加入内容,模板大概是这个样子:

1
2
3
4
5
#!/bin/sh

# do something

exit 0

刷新服务并使其开机自启:

1
2
$ sudo systemctl daemon-reload
$ sudo systemctl enable rc-local.service