学习整理一下关于后台任务相关的内容,进一步的分析必须从Linux中的进程,信号等机制出发,但是我对此没啥兴趣,这里只是从实用的角度进行学习,浅尝辄止。

通常在shell中执行的任务都是前台任务,即任务会占用前台,在任务结束之前无法进行下一个任务。在命令结尾使用&可以将命令使用后台进程执行,例如

1
./test.sh &

其中的测试脚本在不断写入日志,每隔4秒写入一次

1
2
3
4
5
6
7
8
#!/bin/bash

logfile="time_log.txt"

while true; do
echo "$(date)" >> "$logfile"
sleep 4
done

使用jobs命令可以查看当前的后台任务,每一个后台任务都有独立的序号(注意不是pid),例如

1
[1]+  Running                 ./test.sh &

如果返回值为空,代表没有后台任务。后台任务的状态可能是Running(正在运行),也可能是Stopped(挂起,暂停),Terminated(终止),终止的任务稍后就会从jobs的输出中消失。

需要注意的是,后台任务仍然会向当前终端的输出流写入信息!这可能会混淆其他命令的输出,对于后台任务,最好还是将输出重定向比较稳妥

1
./test.sh > output.log 2>&1 &

如果当前的前台任务太长或者卡死了,我们可以使用ctrl+c强行结束前台任务,也可以使用ctrl+z将前台任务挂起,此时任务会转为后台任务,并且设置为暂停状态,此时jobs输出形如

1
[1]+  Stopped                 ./test.sh

使用fg %1命令可以将指定的后台任务(正在执行或挂起的)转到前台执行。 使用bg %1命令则可以将指定的后台挂起任务在后台执行,这些命令的百分号很重要)

1
2
3
bg %1

fg %1

如果缺省编号,默认操作对象是标记为+的最近任务,此外还有标记为-的任务,含义是如果+标记的任务结束,-标记的任务就会变成+标记。

使用kill命令可以终止后台任务,例如 kill %jobnumberkill PID

需要注意的是,在一个会话中,无论是前台任务还是后台任务都是从属于当前会话的,如果退出当前的登陆,后台任务也会被终止, 有两种方法来解决这种问题:

  • 使用nohup命令;
  • 使用setsid命令。

第一种是基于nohup的方法,可以使用nohup搭配 & 来解决会话退出导致后台任务终止的问题,此时对应的进程不会因为退出会话而被终止,例如

1
nohup ./test.sh  &

nohup命令默认会将输出重定向到当前位置下的nohup.out文件(如果当前位置无权限则会回到家目录下创建nohup.out文件),在事后可以使用nohup.out文件来查看这个后台任务的输出。

可以指定全部输出重定向到指定文件,例如

1
nohup ./test.sh > out.log 2>&1 &

nohup命令有提示信息,需要回车确认一下。

如果对一个已经正在运行的后台任务忘记使用nohup执行了,还可以使用disown命令进行补救,例如

1
2
./test.sh &
disown -h %1

第二种是基于setsid的方法,它的做法在原理上更加彻底,此时的进程和当前会话不再是从属关系,因此当前会话的终止不会影响该进程,例如

1
setsid ./test.sh &

使用小括号似乎有一样的效果

1
(./test.sh &)