shell作业后台执行的方法

来思考几种场景:
1、某个脚本需要执行时间比较长,无人值守,可能执行过程中因ssh会话超时而中断?
2、某次测试一段代码,需要临时放入后台运行?
3、放入后台运行的脚本,需要在一段时间后重新调到前台?
4、已经调起作用后,忽然发现没有将作业放入后台执行,如何补救?
5、需要在后台运行大量的脚本,如何管理?
一、问题分析与思路 当终端关闭或网络断开时,当前终端中运行的进程就会收到SIGHUP(终止信号),终端关闭,终端进程之下的所有子进程也会关闭。
为此,我们可以考虑:
(1)有没有方法让运行中的进程不再收到SIGHUP信号
(2)有没有方法让运行中的进程独立存在,不属于当前终端的子进程
二、shell/命令/作业后台执行方法 1、nohup nohup的用途就是让提交的命令忽略hangup信号。语法如下:

# nohup --help 用法:nohup 命令 [参数]...  或:nohup 选项 忽略挂起信号运行指定的命令。

(1)使用
nohup的使用比较简单,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到nohup.out文件中。
使用时,常见的有以下几种方式:
(1)一般我们可在结尾加上"&"来将命令同时放入后台运行;
(2)">filename 2>&1"来更改缺省的重定向文件名;
(3)直接">/dev/null 2>&1 &"重定向到/dev/null,不记录日志;
注意:
(1)如果不将 nohup 命令的输出重定向,输出将附加到当前目录的 nohup.out 文件中。
(2)如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。
(3)如果没有文件能创建或打开以用于追加,那么 COMMAND参数指定的命令不可调用。
(4)如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。
(2)示例
示例1:直接放入后台运行
[root@vnx ~]# cat a.sh ping www.baidu.com [root@vnx ~]# nohup sh a.sh &##直接放入后台运行 [1] 31159##进程号 [root@vnx ~]# nohup: 忽略输入并把输出追加到"nohup.out"[root@vnx ~]# ps -ef | grep 31159 root31159310510 22:49 pts/000:00:00 sh a.sh root31160311590 22:49 pts/000:00:00 ping www.baidu.com root31280311340 23:06 pts/100:00:00 grep 31159 [root@vnx ~]# ps -ef | grep ping root31160311590 22:49 pts/000:00:00 ping www.baidu.com root31282311340 23:06 pts/100:00:00 grep ping[root@vnx ~]# kill -9 31159 [root@vnx ~]# ps aux | grep 31159 root313310.00.0 103256848 pts/1S+23:110:00 grep 31159 [root@vnx ~]# ps -ef | grep ping root3116010 22:49 pts/000:00:00 ping www.baidu.com root31333311340 23:11 pts/100:00:00 grep ping [root@vnx ~]# tail -f nohup.out 64 bytes from 61.135.169.121: icmp_seq=976 ttl=128 time=1.56 ms 64 bytes from 61.135.169.121: icmp_seq=977 ttl=128 time=2.02 ms

示例2:重定向到文件
[root@vnx ~]# nohup sh a.sh >tmp.log 2>&1 & [1] 31392 [root@vnx ~]# ps -ef | grep 31392 root31392311340 23:21 pts/100:00:00 sh a.sh root31393313920 23:21 pts/100:00:00 ping www.baidu.com root31395311340 23:21 pts/100:00:00 grep 31392 [root@vnx ~]# jobs##查看作业,jobs可以查看nohup提交到后台的作业 [1]+Runningnohup sh a.sh > tmp.log 2>&1 & [root@vnx ~]# fg %1#将jobs查到的作业调入前台 nohup sh a.sh > tmp.log 2>&1

不记录日志方式与此类似,只是将/dev/null作为重定向的目标。
说明:
(1)ctrl + z:可以将一个正在前台执行的命令放到后台,并且暂停 (2)jobs:查看当前有多少在后台运行的命令 jobs 列出当前shell环境中已启动的任务状态,若未指定jobsid,则显示所有活动的任务状态信息;如果报告了一个任务的终止(即任务的状态被标记为Terminated),shell 从当前的shell环境已知的列表中删除任务的进程标识 (3)fg:将后台中的命令调至前台继续运行 如果后台中有多个命令,可以用 fg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid) (4)bg:将一个在后台暂停的命令,变成继续执行 如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)

2、setid setsid能新建一个session,语法如下:
NAME setsid - run a program in a new session SYNOPSIS setsid program [arg...]

示例
[root@vnx ~]# setsid ping www.baidu.com [root@vnx ~]# PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=128 time=3.63 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=128 time=4.12 ms 64 bytes from 220.181.111.188: icmp_seq=3 ttl=128 time=4.18 ms[root@vnx ~]# ps -ef |grep ping root3175810 23:55 ?00:00:00 ping www.baidu.com##注意与nohup的区分,这里的父进程号是1(init进程) root31760317270 23:55 pts/100:00:00 grep ping [root@vnx ~]# jobs#这里使用jobs是无法查到相关的作业信息的

注意:setsid与nohup的区分,这里的进程 ID(PID)为31758,而它的父ID(PPID)为1(即为 init进程ID)并不是当前终端的进程ID
说明:当新建一个session后,相当创建了init进程的一个子进程,与当前终端的session便不再关联,自然,关闭终端,也不会对其产生影响。
shell作业后台执行的方法
文章图片

3、()和& 我们知道在Shell中有这样一个知识点:
(cmd1; cmd2; cmd3)

命令组:在括号中的命令列表,将会作为一个子shell来运行。
(注:在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的。父进程,也就是脚本本身,将不能够读取在子进程中创建的变量, 也就是在子shell中创建的变量。)
示例:
[root@vnx ~]# (ping www.baidu.com )##会话1 PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=128 time=5.11 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=128 time=3.94 ms ^C --- www.a.shifen.com ping statistics --- 19 packets transmitted, 19 received, 0% packet loss, time 18817ms rtt min/avg/max/mdev = 3.408/4.026/5.480/0.539 ms [root@vnx ~]###直接用()包围命令,在会话退出后子命令也是会退出的[root@vnx ~]# ps -ef | grep www.baidu.com##会话2 root54495310510 14:38 pts/000:00:00 ping www.baidu.com##发现子命令的父命令进程是31051 root54497317270 14:39 pts/100:00:00 grep www.baidu.com [root@vnx ~]# jobs##jobs作业列表里面没有作业与之对应
[root@vnx ~]# [root@vnx ~]# ps -ef | grep www.baidu.com##会话1中终止后,会话2中就看不到相关子命令的进程了 root54514317270 14:40 pts/100:00:00 grep www.baidu.com

上面我们知道,&可以将命令放入后台执行,我们尝试将&也放入括号里面:
[root@vnx ~]# (ping www.baidu.com &)##会话1 [root@vnx ~]# PING www.a.shifen.com (61.135.169.125) 56(84) bytes of data. 64 bytes from 61.135.169.125: icmp_seq=1 ttl=128 time=6.73 ms 64 bytes from 61.135.169.125: icmp_seq=2 ttl=128 time=9.75 ms ^C##Ctrl + C不能终止子命令 [root@vnx ~]# 64 bytes from 61.135.169.125: icmp_seq=3 ttl=128 time=3.17 ms 64 bytes from 61.135.169.125: icmp_seq=4 ttl=128 time=3.44 ms 64 bytes from 61.135.169.125: icmp_seq=5 ttl=128 time=1.88 ms[root@vnx ~]# ps -ef | grep www.baidu.com##查看该命令的进程,发现其父进程是1,即init初始进程,故当前终端的hub信号是无法影响到它的 root5453510 14:44 pts/000:00:00 ping www.baidu.com root54537317270 14:45 pts/100:00:00 grep www.baidu.com [root@vnx ~]# jobs##jobs作业列表里面是查询不到的
[root@vnx ~]#

从上面我们可以看到:(cmd1; cmd2; cmd3 &)也可以将命令放入后台执行,效果与setsid类似。
4、disown 若某次操作中,作业已经提交后发现没有放入后台执行,如何补救才能避免被hup信号影响呢?
这种情况下,使用setsid、()+&是为时已晚的,只能使用作业调度和disown来解决这个问题。disown命令的语法如下:
disown jobID disown jobID1 jobID2 ... jobIDN disown [options] jobID1 jobID2 ... jobIDN

disown的可选参数如下:
-aDelete all jobs if jobID is not supplied. -hMark each jobID so that SIGHUP is not sent to the job if the shell receives a SIGHUP.(使某个作业忽略hup信号) -rDelete only running jobs.

man手册说明如下:
disown [-ar] [-h] [jobspec ...] Withoutoptions, each jobspec is removed from the table of active jobs. If jobspec is not present, and neither -a nor -r is supplied, the shell’s notion of the current job is used. If the -h option is given, each jobspec is not removed from the table, but is marked so that SIGHUP is not sent to the jobifthe
shellreceivesaSIGHUP. If no jobspec is present, and neither the -a nor the -r option is supplied, the current job is used. If no jobspec is supplied,the -a option means to remove or mark all jobs; the -r option without a jobspec argument restricts operation to running jobs.The return value is 0 unless a jobspec does not specify a valid job.

下面通过例子进行说明:
(1)没有任何选项的情况下,移除不活动作业
shell作业后台执行的方法
文章图片

(2)所有的作业都忽略hup信号
[root@vnx ~]# jobs -l [1]+ 73743 Runningnohup ping www.baidu.com & [root@vnx ~]# [root@vnx ~]# disown -a [root@vnx ~]# jobs -l

(3)只有正在运行的作业忽略hup信号
[root@vnx ~]# jobs -l [1]- 83039 Runningnohup ping www.baidu.com & [2]+ 83187 停止ping www.baidu.com [root@vnx ~]# disown -r [root@vnx ~]# jobs -l [2]83187 停止ping www.baidu.com

(4)当退出会话时,如何保持作业在后台继续运行
方式一:指定作业ID
# disown -h jobID # disown -h %n ##n为作业号,%%代表当前job

方式二:不指定作业ID
## Step 1: update system ## apt-get upgrade &> /root/system.update.log &## Step 2: Mark apt-get so that SIGHUP is not sent when you exit and go for tea ## disown -h ## Step 3: exit from root shell ## exit

(5)组合使用方式
用disown -h jobspec来使某个作业忽略HUP信号 用disown -ah 来使所有的作业都忽略HUP信号 用disown -rh 来使正在运行的作业忽略HUP信号

注意:(1)当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。
(2)这种方法的操作对象是作业,如果我们在运行命令时在结尾加了"&"来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过jobs命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!
CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs命令来查询它的作业号,再用bg jobspec来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。示例如下:
(6)若提交命令时,已经使用&建命令放入后台执行,则可直接使用disown:
[root@vnx ~]# cp -r /opt/hadoop-2.7.3 /root/ & [1] 62042 [root@vnx ~]# jobs [1]+Runningcp -i -r /opt/hadoop-2.7.3 /root/ & [root@vnx ~]# disown -h %1 [root@vnx ~]# jobs [1]+Runningcp -i -r /opt/hadoop-2.7.3 /root/ & [root@vnx ~]# ps -ef | grep hadoop root6204246781 10 18:51 pts/000:00:01 cp -i -r /opt/hadoop-2.7.3 /root/ root62140467810 18:51 pts/000:00:00 grep hadoop

(7)若提交命令时,未将命令放入后台执行,则先用ctrl + z和bg将命令放入后台执行,然后使用disown:
[root@vnx ~]# cp -r /opt/hadoop-2.7.3/ /root/ ^Z [1]+Stoppedcp -i -r /opt/hadoop-2.7.3/ /root/ [root@vnx ~]# bg %1 [1]+ cp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# jobs [1]+Runningcp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# disown -h %1 [root@vnx ~]# jobs [1]+Runningcp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# ps -ef | grep hadoop root63368586216 18:55 pts/300:00:01 cp -i -r /opt/hadoop-2.7.3/ /root/ root63514586210 18:55 pts/300:00:00 grep hadoop

5、screen screen是建立一个新的全屏虚拟会话终端,这个会话只有在手动输入exit的时候才会退出,在这个会话里执行的命令不用担心HUP信号会对我们的进程造成影响,因此也不用给每个命令前都加上“nohup”或“setsid”了,非常适合我们有规划的执行大量的后台任务,可以非常方便的让我们对这些后台任务进行管理。
使用方法:
screen##立即创建并进入一个会话。 screen -dmS {name}##建立一个处于挂起模式下的会话,并根据我们的需要指定其会话名称。 screen -dmS {name} {script}##在建立会话时同时执行指定的命令或脚本 screen -list##列出所有会话。 screen -r {name}##以独占方式进入指定会话。 screen -x {name}##以并行方式进入指定会话。 ctrl +ad##输入快捷键ctrl +a和d,可暂时退出当前会话。 exit##进入指定会话后执行exit即可关闭该会话。

注:若有兴趣,可尝试用pstree命令查看一下使用screen过程中的进程树,这样会更容易理解!!!
通过以上几种说明,可总结几种情况的使用情境:
nohup/setsid 适用于作业量比较少的情况下; ()+&适用于测试或写脚本过程中,临时使用; disown适用于事后补救当前已经在运行的作业和命令的情况; screen最适合有大批量操作时的情况。

为方便理解,可阅读以下参考文档:
Linux 技巧:让进程在后台可靠运行的几种方法(nohup等)http://blog.csdn.net/zstu_zlj/article/details/51783937
Linux学习:让进程在后台运行的几种方法
fg bg ctrl + z jobs & 等命令http://www.cnblogs.com/xlmeng1988/archive/2012/06/04/jobs.html
linux中的(),(()),[],[[]],{}的作用http://blog.csdn.net/damotiansheng/article/details/44196769
Linux / Unix: disown Command Examples – nixCrafthttps://www.cyberciti.biz/faq/unix-linux-disown-command-examples-usage-syntax/
Linux运行与控制后台进程的方法:nohup, setsid, &, disown, screen ? Hey! Linux.http://www.heylinux.com/archives/1282.html
本文原始链接:http://www.cnblogs.com/chinas/p/7130378.html,转载请注明出处,谢谢!!!!
【shell作业后台执行的方法】转载于:https://www.cnblogs.com/chinas/p/7130378.html

    推荐阅读