后台运行脚本

之前在网上找了一个用来薅羊毛的脚本,ssh连接上服务器之后开始运行脚本,但是如果你logout一下退出了shell,那其实这个脚本是不会继续运行的。之后去网上找了一下,只需要如此运行即可nohup your-script.sh &,这样就算你退出了shell也不会影响脚本的执行,今天有空就来稍微整理一下这其中的原理。

前台和后台任务

首先我自己写了一个每隔一秒输出当前时间的python脚本和bash脚本,用来作为测试

Python:

1
2
3
4
5
6
7
8
#!/usr/bin/env python
# -*- coding=utf-8 -*-
import datetime
import time
while True:
now_time = datetime.datetime.now()
print("time = " + now_time.strftime('%Y.%m.%d-%H:%M:%S'))
time.sleep(1)

Bash:

1
2
3
4
5
6
7
8
#!/bin/bash
# this script is used to print time every second

while [ true ]; do
/bin/sleep 1
time=$(date "+%Y-%m-%d %H:%M:%S")
echo "${time}"
done

如果直接运行,效果就是这样的:

1569036707821

确实是想要的效果,但是一旦退出,这个程序也就没了,所以如果希望这个程序一直打印,可以使它成为守护进程(daemon),或者说服务(service)。

之前直接在命令行输入的,会直接输出在标准输出上,这种叫做前台任务。而第一步就是希望它能够到后台去运行。需要达成这个目的还是非常容易的,直接在命令的最后加上一个&即可。

1569037010443

发现加了&之后系统会告诉你一个任务的序号,直接用fg就可以让它重新来到前台工作。

那么后台任务有什么特点呢?

  • 继承当前 session 的标准输出(stdout)和标准错误(stderr)。由上图可知我们的屏幕还是会显示输出的时间,因为后台任务继承了标准输出。

  • 不继承当前 session 的标准输入(stdin)。所以此时你可以继续输入命令,比如ls来查看当前目录的文件啦,但是也意味着你无法对这个程序进行操作了。

所以,就算程序成为了后台程序,但是它还是和标准输出有关系,而退出shell的同时,由于该程序无法找到标准输出,所以还是会报错并且挂掉,如果重定向一下就不会挂掉了。所以只要后台任务不和标准输出、标准错误打交道,是不是意味着就可以持续运行呢?那之前为什么还需要加上nohup呢?

nohup

在介绍这个命令之前,先对用户退出shell的时候执行的操作顺序进行一波总结:

  1. 用户准备退出 session
  2. 系统向该 session 发出SIGHUP信号
  3. session 将SIGHUP信号发给所有子进程
  4. 子进程收到SIGHUP信号后,自动退出

也就是,其实关键就在于你的session有没有发送SIGHUP给进程,如果发了,那不管它是不是后台进程,它都得玩完。也可以从之前的实验结果看出,我的那台机器,是默认不发送SIGHUP信号给后台程序的,具体可以通过shopt | grep huponexit这条命令查看你的系统在退出的时候会不会发送信号给进程。

所以一个真正在后台运行的程序需要这么做:不和标准IO进行交互,不接受SIGHUP信号。只要满足这两点即可,而nohup这个命令设计就是为了实现这两点。它阻止系统发送信号给程序,同时它会帮你重定向标准输出。

If standard input is a terminal, redirect it from an unreadable file. If standard outputis a terminal, append output to ‘nohup.out’ if possible, ‘$HOME/nohup.out’ otherwise. If standard error is a terminal, redirect it to standard output.

以上是man手册的内容,所以从上面也可以推断出,如果你的程序在你退出之后发生了错误,是会被定为到标准输出的,而此时标准输出已经不存在了,所以程序还是会退出哦。

另外的方法

可以使用at或者cron等定时任务,或者也可以使用tmux这种工具。