Linux / Unix 系统有许多后台运行命令

例如 nohup、&、disown、setsid、screen、jobs 

本文将介绍他们之间的区别和联系

 

使用场景:

我们经常会碰到这样的问题,用ssh登录了远程的Linux服务器,运行了一些耗时较长的任务,结果却由于网络等的不稳定导致任务中途失败。

这是由于在用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。

解决办法有两种:

1)让进程忽略HUP信号,如 nohup、screen

2)让进程运行在新的会话里执行,从而不属于此终端的子进程,如 setsid、(cmd &)

 

1. nohup

用途:不挂断地运行命令,即退出了终端仍然会在后台运行,常配合&使用。

语法:nohup Command [ Arg … ] [ & ]

无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。

注意点:

1)如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。

2)如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。

退出状态,则该命令返回下列出口值: 

126 可以查找但不能调用 Command 参数指定的命令。 

127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。 

否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。

nohup 命令,会将标准输出和标准错误默认重写向到nohup.out中,所以可以在命令之后添加上">filename 2>&1",例如把输出重新定向到/mimvp-nohup.log文件中。

nohup ~/mimvp-shell.sh > ~/mimvp-nohup.log 2>&1 &

 

2. & 和 ()

用途:在后台运行,但有一个问题ssh窗口关闭或网络断开,此后台程序也会停止运行。

原因:当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。

解决办法:让进程忽略HUP信号或者将对应脚本程序设置为不属于此终端的子进程。

一般 nohup 和 & 两者一起配合使用,例如:

nohup command &

另外补充:

&可以结合括号()一起使用,产生一个新的子shell并在此子shell中将任务放置到后台运行,从而不受当前shell终端的HUP信号影响,此用法类似与 setsid,且通过 jobs -l无法查看,即提交的作业不在作业列表里。

使用格式:(COMMAND [ARG]... &)

应用实例:(sh mimvp-shell.sh &)

运行结果:

$ ps -ef | grep mimvp
homer    10955     1  0 18:04 pts/5    00:00:00 sh mimvp-shell.sh
homer    10956 10955  0 18:04 pts/5    00:00:00 ping mimvp.com
homer    10992 10957  0 18:04 pts/24   00:00:00 grep --color=auto mimvp

可见,其父进程id=1,并非当前的终端id,从而不受当前终端影响,关闭并重启终端后,命令会仍然在后台运行,非常类似于 setsid

而我通常的使用方式为:
nohup ./filename.sh > filename.log 2>&1 &
nohup ./filename.sh &> filename.log &

nohup 优于 & 的三点理由:
1) nohup保障进程不会被hangup信号异常中断;
2) 将任务放置到后台运行,不占用当前的终端;
3) 将错误输出也打印到log中,默认>只有标准输出,错误输出没有。

 

3. jobs

使用格式:jobs [-lnprs] [jobspec ...] or jobs -x command [args]

jobs 用于查看当前进程,且只看当前终端生效的,关闭终端后在另一个终端jobs已经无法看到后台跑得程序了,那时需利用ps

控制进程(disown 中经常使用)

查看当前终端下的后台进程:
直接执行:jobs -l

切换到前台,将查看到的某个后台进程放回到前台:
直接输入:fg {jobid}  // 这里的{jobid}是通过jobs命令中看到的进程前[]中的数字

切换到后台,将当前正在前台运行的进程放到后台运行:
先敲下快捷键:ctrl +z    //  暂停当前正在运行的进程
再执行:bg 

终止当前正在前台运行的进程:
直接敲下快捷键:ctrl +c

 

应用实例:

/usr/bin/Xvfb :7 -ac -screen 0 1280x1024x8 > /dev/null 2>&1 &
export DISPLAY=:7
nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &

运行查看日志:

$ nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &
[1] 10020
$ nohup: ignoring input and appending output to ‘nohup.out’
$ 
$ ll
-rw-r--r--  1 homer homer  3730403 12月 24 23:18 chromedriver_linux64_v2.34.zip
-rw-rw-r--  1 homer homer       90 12月 26 22:26 composer.json
-rw-rw-r--  1 homer homer    41340 12月 26 22:31 composer.lock
-rwxr-xr-x  1 homer homer  1855013 12月 26 22:25 composer.phar*
-rw-rw-r--  1 homer homer  2259579  1月  2 12:10 geckodriver-v0.19.0-linux64.tar.gz
-rw-r--r--  1 homer homer  2301226 12月 25 22:56 geckodriver-v0.19.1-linux64.tar.gz
-rw-------  1 homer homer    22448  1月  2 16:52 nohup.out
-rw-rw-r--  1 homer homer 22844105  1月  2 11:53 selenium-server-standalone-3.8.0.jar
-rw-rw-r--  1 homer homer 22844868 12月 28 13:31 selenium-server-standalone-3.8.1.jar
drwxrwxr-x 12 homer homer     4096 12月 26 22:31 vendor/
$ jobs -l
[1]+ 10020 Running                 nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &

日志可见,自动在当前目录下,创建了日志文件 nohup.out 

关闭当前终端,然后再打开终端,查看 nohup 是否仍然在运行(没有被挂断)

$ ps -ef | grep java
homer     10020     1  0 16:59 ?        00:00:04 java -jar selenium-server-standalone-3.8.0.jar -port 8888

确认了,nohup 和 & 配合使用,关闭终端后再重启终端,java 仍然一直在后台运行,没有被挂断

 

4. setsid

setsid - run a program in a new session

setsid 是让提交的命令归属一个新会话,即新开一个终端,其用法与nohup类似。

$ setsid java -jar selenium-server-standalone-3.8.0.jar -port 8888 &   
[2] 10269
$ 17:23:40.735 INFO - Selenium build info: version: '3.8.0', revision: '924c4067df'

不同的是 setsid 是新开一个终端会话,nohup是在当前的终端会话

例如:对比下进程父ID(PPID)

$ ps -ef | grep java
homer    10020  9859  0 17:05 pts/1    00:00:02 java -jar selenium-server-standalone-3.8.0.jar -port 8888
homer    10270     1 22 17:23 ?        00:00:01 java -jar selenium-server-standalone-3.8.0.jar -port 8888
homer    10291  9859  0 17:23 pts/1    00:00:00 grep --color=auto java

通过对比可看出,使用setsid提交的命令父进程id为1,并不是当前终端进程id;而 nohup提交的命令父进程id为当前终端id=9859

 

5. disown

使用格式:disown [-h] [-ar] [jobspec ...]

如果未加任何处理就已经提交了命令,可使用disown补救,为没有使用nohup与setsid的进程加上忽略HUP信号的功能,例如提交的命令可用&放入后台运行

如果执行的命令想在前台和后台切换,也可以使用disown进行控制切换。

应用实例:

创建一个ping脚本进行运行模拟

vim mimvp-shell.sh

#!/bin/bash
ping mimvp.com

一般后台运行

$ sh mimvp-shell.sh &
[1] 12189
$ jobs -l
[1]+ 12189 Running                 sh mimvp-shell.sh &

当前终端关闭后,此进程会随之关闭,终止

 

disown 后台运行

$ sh mimvp-shell.sh &
[1] 12357
$ jobs -l
[1]+ 12357 Running                 sh mimvp-shell.sh &
$
$ disown -h %1
$
$ jobs -l
[1]+ 12357 Running                 sh mimvp-shell.sh &
$
$ ps -ef | grep mimvp
homer    12357 12279  0 19:15 pts/24   00:00:00 sh mimvp-shell.sh
homer    12358 12357  0 19:15 pts/24   00:00:00 ping mimvp.com
homer    12385 12279  0 19:18 pts/24   00:00:00 grep --color=auto mimvp

如上,执行 disown -h %1 后,通过 jobs -l 查看进程号前后几乎没有变化,ps 查看进程号也几乎没有变化

接着,关闭终端,并重启终端后,通过 ps 查看进程 sh mimvp-shell.sh

$ ps -ef | grep mimvp
homer    12357     1  0 19:15 ?        00:00:00 sh mimvp-shell.sh
homer    12358 12357  0 19:15 ?        00:00:00 ping mimvp.com
homer    12534 12498  0 19:22 pts/5    00:00:00 grep --color=auto mimvp

发现进程 sh mimvp-shell.sh 仍然在运行,且其父进程id=1

即执行 disown -h %1 命令后,让作业号为1的进程,转为了后台nohup进程,即成功实现了亡羊补牢

实现了功能: 后台命令 —> nohup 后台运行

 

disown 进一步的亡羊补牢

$ sh mimvp-shell.sh 
PING mimvp.com (47.95.6.112) 56(84) bytes of data.
64 bytes from 47.95.6.112: icmp_seq=1 ttl=53 time=8.87 ms
64 bytes from 47.95.6.112: icmp_seq=2 ttl=53 time=8.94 ms
64 bytes from 47.95.6.112: icmp_seq=3 ttl=53 time=11.9 ms
^Z         (Ctrl + z 挂起前台进程)
[1]+  Stopped                 sh mimvp-shell.sh
$ bg 1     (bg 1 切换到后台进程; fg 切换到前台进程)
[1]+ sh mimvp-shell.sh &
$ 64 bytes from 47.95.6.112: icmp_seq=4 ttl=53 time=9.07 ms
$ disown  -h %1
$ jobs -l
[1]+ 12800 Running                 sh mimvp-shell.sh &

说明:

1) 一开始在前台运行命令 sh mimvp-shell.sh

2) Ctrl + z 挂起前台进程

3) bg 1 (bg 1 切换到后台进程; fg 切换到前台进程)

4) disown -h %1  后台进程挂起

5) 关闭终端,重启终端后,仍然在后台执行

$ ps -ef | grep mimvp
homer    12800     1  0 19:30 ?        00:00:00 sh mimvp-shell.sh
homer    12801 12800  0 19:30 ?        00:00:00 ping mimvp.com
homer    12971 12927  0 19:37 pts/24   00:00:00 grep --color=auto mimvp

实现了功能: 前台命令 —> 后台命令 —> nohup 后台运行

补充说明:

jobs -l 查看当前作业一般接-l,用于显示其作业号
bg %作业号,将作业在后台运行,例如 bg 1, bg %1
fg %作业号,将作业在前面处理,例如 fg 1, fg %1
CTRL-z,将当前程序挂起

 

6. screen

screen是建立一个新的全屏虚拟会话终端,这个会话只有在手动输入exit的时候才会退出,在这个会话里执行的命令不用担心HUP信号会对我们的进程造成影响,因此也不用给每个命令前都加上“nohup”或“setsid”了,非常适合我们有规划的执行大量的后台任务,可以非常方便的让我们对这些后台任务进行管理。

Ubuntu 安装命令:

sudo apt-get -y install screen

 

使用方法:Linux screen 命令详解

screen //立即创建并进入一个会话。
screen -dmS {name} //建立一个处于挂起模式下的会话,并根据我们的需要指定其会话名称。
screen -dmS {name} {script} //在建立会话时同时执行指定的命令或脚本
screen -list //列出所有会话。
screen -r {name} //以独占方式进入指定会话。
screen -x {name} //以并行方式进入指定会话。
ctrl +ad //输入快捷键ctrl +a和d,可暂时退出当前会话。
exit //进入指定会话后执行exit即可关闭该会话。

screen - screen manager with VT100/ANSI terminal emulation

Screen is a full-screen window manager that multiplexes a physical terminal between several processes (typically interactive shells).  Each virtual terminal provides the functions of a DEC VT100 terminal and,  in  addition,  several  control  functions from the ISO 6429 (ECMA 48, ANSI X3.64) and ISO 2022 standards (e.g. insert/delete line and support for multiple character sets).  There is  a  scrollback history  buffer  for  each  virtual  terminal  and a copy-and-paste mechanism that allows moving text regions between windows.

$ screen --help              
Use: screen [-opts] [cmd [args]]
 or: screen -r [host.tty]

Options:
-4            Resolve hostnames only to IPv4 addresses.
-6            Resolve hostnames only to IPv6 addresses.
-a            Force all capabilities into each window's termcap.
-A -[r|R]     Adapt all windows to the new display width & height.
-c file       Read configuration file instead of '.screenrc'.
-d (-r)       Detach the elsewhere running screen (and reattach here).
-dmS name     Start as daemon: Screen session in detached mode.
-D (-r)       Detach and logout remote (and reattach here).
-D -RR        Do whatever is needed to get a screen session.
-e xy         Change command characters.
-f            Flow control on, -fn = off, -fa = auto.
-h lines      Set the size of the scrollback history buffer.
-i            Interrupt output sooner when flow control is on.
-l            Login mode on (update /var/run/utmp), -ln = off.
-ls [match]   or -list. Do nothing, just list our SockDir [on possible matches].
-L            Turn on output logging.
-m            ignore $STY variable, do create a new screen session.
-O            Choose optimal output rather than exact vt100 emulation.
-p window     Preselect the named window if it exists.
-q            Quiet startup. Exits with non-zero return code if unsuccessful.
-r [session]  Reattach to a detached screen process.
-R            Reattach if possible, otherwise start a new session.
-s shell      Shell to execute rather than $SHELL.
-S sockname   Name this session <pid>.sockname instead of <pid>.<tty>.<host>.
-t title      Set title. (window's name).
-T term       Use term as $TERM for windows, rather than "screen".
-U            Tell screen to use UTF-8 encoding.
-v            Print "Screen version 4.01.00devel (GNU) 2-May-06".
-wipe [match] Do nothing, just clean up SockDir [on possible matches].
-x            Attach to a not detached screen. (Multi display mode).
-X            Execute <cmd> as a screen command in the specified session.

 

 

参考推荐

Difference between nohup, disown and &

Linux screen 命令详解

Linux Shell1>/dev/null2>&1含义

Linux用户都应该了解的命令行省时技巧

PHP + Selenium + WebDriver 抓取米扑科技首页