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 1
esac
exit 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_shutdown
exit 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 shutdown
Requires=network.target
DefaultDependencies=no
Before=shutdown.target reboot.target
 
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true
ExecStop=mycommand

[Install]
WantedBy=multi-user.target

补充:Shutdown Sequence Systemd

1.首先创建一个触发服务(该服务需要是关机服务) 

# cat /lib/systemd/system/fakehalt.service

[Unit]
Description=Fake-Halt Service
After=fakevm.service
Requires=fakevm.service

[Service]
Type=simple
ExecStart=/usr/local/bin/fakehalt.sh #this will fail until fakevm succeeds
ExecReload=/usr/local/bin/fakehalt.sh

 2.然后创建一个系统允许的你想要在关机之前运行完成的命令文件

# cat /lib/systemd/system/fake.service
[Unit]
Description=Fake Service
Before=fakehalt.service

[Service]
Type=simple
ExecStart=/usr/local/bin/fake.sh

3.创建一个脚本来表示虚拟机关机(或任何你不能预见其持续时间的进程)

# cat /usr/local/bin/fake.sh
#!/bin/sh

test="1"
sleep 21
if [ X"$test" = "X1" ]; then
    echo "vm has shut down" > /tmp/fake.test
    exit 0
else 
    exit 1
fi

和一个用于关闭信号的脚本:

# cat /usr/local/bin/fakehalt.sh
#!/bin/sh

sleep 3
cat /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已经成功执行了。