在shell退出前,交互的 shell 向所有作业,运行的或停止的,发送SIGHUP信号。shell 向停止的作业发出SIGCONT信号来保证它们会收到SIGHUP
假如想要启动的进程在shell退出之后还可以继续运行,除了tmux和screen有三种方法
setsid命令之所以能让子进程在shell退出后还可运行并不是因为setsid(2)的作用,因为setsid的实现默认是进行一次fork然后让父进程退出,子进程运行exec系列函数,这样用setsid运行的命令就是shell的子子进程,所以不会收到SIGHUP
nohup使用signal系列函数忽略了SIGHUP信号
disown是个buildin命令使作业不会收到SIGHUP信号
现有很多答案都说的不错,不过有一个共同点就是都是告诉你一开始你就打算后台运行的时候怎么做。
作为一个经常掉链子的蠢人,我是来给你送后悔药的。
当你一开始没打算在后台长期执行一个命令,但是运行了之后发现花很多时间,但又不想在那里傻等,怎么办?(这种事通常发生在下班前2小时执行了一个命令,然后跑去撩漂亮的前台姑娘,快下班回来一看还没完,一打听才发现这货居然要执行超过8小时……知识都是在惨痛的教训中获得的!)
首先,你要是还想要程序的输出,那你要么继续傻等,要么停掉现在执行的进程,按照其他答案说的,用nohup或者用tmux/screen一类的工具重新来过。
如果程序的输出无所谓,你只要程序能继续执行下去就好——典型的例子是你压缩一个文件或者编译一个大软件,中途发现需要花很长时间——那你接着看我给你的秘方。
下面是一个实例,你可以看看。
bash-3.2$ sleep 3600 # 要执行很久的命令 ^Z [1]+ Stopped sleep 3600 bash-3.2$ jobs [1]+ Stopped sleep 3600 bash-3.2$ bg %1 [1]+ sleep 3600 & bash-3.2$ jobs [1]+ Running sleep 3600 & bash-3.2$ disown bash-3.2$ jobs bash-3.2$ ps -ef | grep sleep # 此处输出可知,那个命令还在执行 501 30787 30419 0 6:00PM ttys000 0:00.00 sleep 3600 501 33681 30419 0 6:02PM ttys000 0:00.00 grep sleep bash-3.2$ exit
此处重启一个终端窗口
bash-3.2$ ps -ef|grep sleep 501 30787 1 0 6:00PM ?? 0:00.00 sleep 3600 501 36701 36592 0 6:05PM ttys001 0:00.00 grep sleep bash-3.2$
可以看到刚才的命令还在执行。
这个方法,可以让你在后知后觉发现一个命令要执行很久的时候,也可以半路让它改成后台执行。
还有一个好处是,对于一些初期有些需要输入交互的程序,用这个方法在后期长期执行的时候转入后台,可以免去一开始就后台执行导致无法输入信息的烦恼。至于怎么在后台交互(尤其是sudo之类需要输入密码的),那就是另一个问题了……不过,一定要注意,要确定所有交互都结束了再转入后台,不然那个进程会停在等待输入的地方直到被人杀掉或者系统关闭……
但是,这个方法的缺点就是无法获取程序输出了。所以,如果你用bash或者类似兼容的shell,在执行命令后面加一个 |& tee program.log 是个好习惯。随时把标准和错误输出导入到日志文件中,一方面可以防翻页太快缓冲太少,一方面可以半路后悔切后台,还有一个终极作用是可以留下证据防坏人。
哦,对了,提醒一下。如果你不是连的远程服务器,而是本地计算机。那么关了终端窗口,但不要关机,关机了,你的程序还是会停的……(感觉我在说废话……不过,做过各种错事之后,你会发现有些废话还是很重要的。)也不要休眠,休眠了程序就暂停了……虽然不至于彻底灰飞烟灭。
重新看了一眼其他答案,我来补充一下nohup的正确使用方法。(发现现有的某些答案很是蔫损坏……)
nohup COMMAND > stdout.log 2> stderr.log &
上面这种,将会把COMMAND命令的标准输出输出到 stdout.log中,错误输出输出到 stderr.log中。
nohup COMMAND > output.log 2>&1 &
这个是把标准输出和错误输出都一股脑地输出到output.log文件中。
推荐上面两种写法,自己指定输出文件。如果你确实很懒,那就直接
nohup COMMAND &
这个时候,nohup命令会默认把标准输出写入nohup.out文件,文件在你执行命令的路径下,也就是Working Directory。至于错误信息……忘了输出到哪里了,好像是nohup.err还是nohup.error的……因为不常用,不记得了。
之所以不推荐默认,是因为会出现预期外的文件,另外,你要是执行好几个,输出文件里就是一锅粥了……
ps. nohup是no hang up的缩写。hang up就是当你关终端的时候,会发给进程的信号名称(通常记作SIGHUP)。no hang up就是不要发这个信号的意思。
pps. 某答案里的那个 /dev/null 是Linux里的黑洞,啥玩意儿扔进去都没了。所以,你不想要输出的时候,可以用。
至于tmux和screen,这两个东西的使用方法需要一本手册,所以有兴趣的人自己去查吧。(其实是我自己也没记全……就记得了一个Ctrl-B了……)
另外,如果你拥有那台服务器的全权管理权,记得应该有个什么设置,让终端退出的时候可以不发HUP信号。我在一台服务器上见过这种现象,不需要输入 disown ,或者不用nohup,直接后台就可以退出不死(前台程序还是会死的)。但我不知道是怎么设置的,等我哪天有兴趣查明白了再回来补充吧……或者哪位高人评论补充一下。
这个问题我查明白了,回来更新一波。
首先,这个是基于bash来说的,其他shell可能情况不同,目前我也只查了bash的情况。
bash里有一个选项,叫 huponexit 如果这个选项设置成 off ,当且仅当正常退出shell的时候(输入exit命令退出或者以Ctrl-d退出)不会像后台进程发送SIGHUP(hang up),进程会得到保留继续执行。如果是异常掉线,强行关闭终端窗口,则会发送SIGHUP,导致后台进程被杀。
貌似默认的bash是 huponexit=off 的,所以有些答案告诉只要在命令后面加 & 就可以了。就是基于此的。然而,无法防止意外掉线,尤其是网络不稳定的地方,这个事儿很不靠谱。因此,上面的答案还是比较推荐的做法。
再说说这个选项如何查看和设置。
$ shopt huponexit huponexit off
上面这个命令可以查看当前设置,第二行是结果。目前是off。
$ shopt -s huponexit $ shopt huponexit huponexit on
上面这段是把这个选项开启,设置为on,并查看确认。
$ shopt -u huponexit $ shopt huponexit huponexit off
这段则是把这个选项关闭,设置为off,并查看确认。
参考链接:
How bash handles the jobs when logout?
What happens to background jobs after exiting the shell?
In which cases is SIGHUP not sent to a job when you log out?
又想起一个可以完成这个任务的方法,更新补充一下。虽然这个不是专门干这个用的。
可以用 at 命令。at命令原本是用来定时执行任务的,但可以使用 at now 来让任务立刻执行,这个时候执行的进程也是在后台而不依赖于当前shell。
具体写法如下:
$ at now at> sleep 300 at> <EOT> # ← 此处按下Ctrl-d结束输入 job 625 at Tue Feb 2 10:24:00 2021 acros800@CY-UATBAT:~ $ ps -ef|grep sleep acros800 23305 23304 0 10:24 ? 00:00:00 sleep 300 acros800 23311 23218 0 10:24 pts/0 00:00:00 grep --color=auto sleep
任务执行后,会将执行时的输出以邮件的形式发到执行者的服务期内邮箱中,可以用 mail 命令查看。
缺点是,执行的时候的环境可能跟shell下直接执行会有所不同(具体它加载的是什么环境,记不清了……可以用atq来查看在执行或预订执行的列表,然后用at -c JOB_ID来查看具体环境配置及命令)。如果执行的是bash的shell script,可以使用 bash -l SCRIPT_FILE 来带上登录时加载的环境项。
忽然发现被推荐为优质内容了,是不是应该加个广告?
比如……
不过,我都没看过,好不好用,大家自行甄别。实在人不打诳语。
话说回来,开卷有益。
这个问题透着一股邪乎味儿,怎么看怎么危险啊……
建议专业机构给予支援。