Linux和Unix系统一直很容易在启动时执行命令,只需要将你的命令添加到 /etc/rc.local 文件中。但事实证明,在关机时运行命令有点复杂。
你为什么想要在计算机关闭时运行命令呢?也许你想从数据库去注册一个机器或服务,也许你想将数据从一个易失性存储系统复制到一个永久的位置(比如将内存中的数据写入到硬盘中去),或者想要你的电脑在它关机前在它的Twitter账户上发布"#RIP me!"
我需要澄清一点,我说的是所谓的“一次性”命令而不是停止守护进程。尽管把他们想象成一样是诱人的。如果你熟悉SysVinit,你可能会想“哦,我能直接创建一个Kill脚本”。例如,当你的系统在运行级别3退出时/ etc / rc.d/rc3.d/k99runmycommandatshutdown会调用。毕竟这就是/etc/init.d/中的脚本如何停止的。
这是一个腻害的猜测,然而事实证明这是错误的。SysVinit并不盲目运行Kill脚本。相反,它 (RedHat 6)会在 /var/lock/subsys/service_name (此处的service_name假设为runmycommandatshutdown)中查找可执行的相关命令。所以你可能会在让系统将你的脚本当作普通服务上遇到一些麻烦。下面的脚本给出了一个例子:
#!/bin/sh# chkconfig: 2345 20 80# description: An example init script to run a command at shutdown # runmycommandatshutdown runs a command at shutdown. Very creative. LOCKFILE=/var/lock/subsys/start(){ # Touch our lock file so that stopping will work correctly touch ${LOCKFILE} } stop(){ # Remove our lock file rm ${LOCKFILE} # Run that command that we wanted to run mycommand } case "$1" in start) start;; stop) stop;; *) echo $"Usage: $0 {start|stop}" exit 1esacexit 0
之后将这个脚本放到 /etc/init.d/runmycommandatshutdown 中,然后 chkconfig on runmycommandatshutdown 使其可用,你的命令就会在关机时运行。
在Slackware (sysvinit in BSD compatible mode)中更容易实现:
$ cat /etc/rc.d/rc.local_shutdown#!/bin/sh## /etc/rc.d/rc.local_shutdown: Local system shutdown script.## Put any local shutdown commands in here.my_command_at_shutdownexit 0$ sudo chmod +x /etc/rc.d/rc.local_shutdown
对于systemd
这一切都很不错,但是如果你运行的Linux版本使用Systemd而不是SysVinit呢?实际上在Systemd中更简单。你所要做的就是将你的脚本放到由systemd-halt.service所管理的 /usr/lib/systemd/system-shutdown/ 目录中去。当然,如果你需要在一个特定的情况下去管理依赖关系(例如你无法在没网的情况下发推特),这时你可以写一个系统服务组文件。例如:
[Unit]Description=Run mycommand at shutdownRequires=network.targetDefaultDependencies=noBefore=shutdown.target reboot.target [Service]Type=oneshotRemainAfterExit=trueExecStart=/bin/trueExecStop=mycommand[Install]WantedBy=multi-user.target
补充:
1.首先创建一个触发服务(该服务需要是关机服务)
# cat /lib/systemd/system/fakehalt.service[Unit]Description=Fake-Halt ServiceAfter=fakevm.serviceRequires=fakevm.service[Service]Type=simpleExecStart=/usr/local/bin/fakehalt.sh #this will fail until fakevm succeedsExecReload=/usr/local/bin/fakehalt.sh
2.然后创建一个系统允许的你想要在关机之前运行完成的命令文件
# cat /lib/systemd/system/fake.service[Unit]Description=Fake ServiceBefore=fakehalt.service[Service]Type=simpleExecStart=/usr/local/bin/fake.sh
3.创建一个脚本来表示虚拟机关机(或任何你不能预见其持续时间的进程)
# cat /usr/local/bin/fake.sh#!/bin/shtest="1"sleep 21if [ X"$test" = "X1" ]; then echo "vm has shut down" > /tmp/fake.test exit 0else exit 1fi
和一个用于关闭信号的脚本:
# cat /usr/local/bin/fakehalt.sh#!/bin/shsleep 3cat /tmp/vmfake.test > /tmp/haltfake.test
4.启动
# systemctl start fakehalt
有可能fakehalt找不到一个叫 /tmp/fake.test 的文件,然后一切都乱套了。实际情况是系统会保持 fakehalt 服务运行直到收到了 exit 0 信号。因此如果你在fakehalt开始后等21秒之后cat /tmp/fakehalt.test ,你会发现/tmp/fakehalt.test并不存在,因为fakehalt已经成功执行了。