如何将一个已经在运行的进程放到nohup下?

1086

我有一个已经运行了很长时间并且不想结束的进程。

我该如何将它放在nohup下(也就是说,如果我关闭终端,它仍然可以继续运行)?


42
面对同样的问题:记住,即使你键入yourExecutable &,输出持续出现在屏幕上,而且Ctrl+C似乎无法停止任何东西,只需盲目地键入disown;并按下Enter,即使屏幕正在滚动输出,你也看不到你正在输入什么。该进程将被取消关联,并且您可以关闭终端而不使该进程终止。 - Nav
10个回答

1552

使用bash的作业控制(Job Control)将进程发送到后台:

  1. Ctrl+Z暂停程序并返回shell。
  2. bg在后台运行程序。
  3. disown -h [job-spec]其中[job-spec]是作业号(如第一个运行的作业为%1,可以用jobs命令查看),以便关闭终端时不会杀死该作业。

42
由于问题是如何“将其放在nohup下”,disown -h可能是更准确的答案:“使disown的行为更像nohup(即作业将留在当前shell进程树中,直到您退出shell),这样您就可以看到此shell启动的所有作业。”(来自[http://www.quantprinciple.com/invest/index.php/docs/tipsandtricks/unix/jobcontrol/]) - Dr. Jan-Philip Gehrcke
12
我如何恢复后台运行的任务?我能够使用 ps -e 命令看到它正在运行。 - Paulo Casaretto
34
在使用 disown 命令后,你将无法查看作业的输出。disown 命令会将进程变为守护进程,这意味着标准输入/输出会被重定向到 /dev/null。因此,如果你计划使用 disown 命令来处理作业,最好在启动作业时将其日志记录到文件中,例如:my_job_command | tee my_job.log - rustyx
9
命令已经在运行后,是否还有可能执行类似于“my_job_command | tee my_job.log”的操作? - arod
29
disown 命令会将进程与其任何管道分离。如需重新附加管道,请按照此线程中所述,使用 gdb 工具。更具体地,参考这篇文章 - brandeded
显示剩余10条评论

216
假设由于某种原因,Ctrl+Z也无法使用,前往另一终端,查找进程 ID(使用ps命令),并运行以下命令:
kill -SIGSTOP PID 
kill -SIGCONT PID

SIGSTOP命令将暂停进程,SIGCONT命令将在后台恢复进程。这意味着现在,关闭你的两个终端不会停止你的进程。


10
是的,这是一个操作系统的问题,在Windows的Cygwin环境下kill命令无法使用。 - Pungs
6
如果工作是从另一个ssh会话开始的,这也非常有用。 - Amir Ali Akbari
7
在关闭第一个终端之前,只需记得在其中执行disown %1即可。 - fred
1
这很有用,因为我启动了一个带控制台的图形界面(在我的情况下,我从 konsole kwin 启动了一个崩溃后没有考虑后果)。因此,如果我停止 kwin,一切都会冻结,我没有运行 bg 的可能性! - Michele
2
更容易记住的是 kill -SIGTSTP PIDkill -SIGCONT PID - Pablo Bianchi
显示剩余5条评论

102

将正在运行的作业与shell分离(即使其成为nohup)的命令是disown,这是一个基本的shell命令。

来自bash-manpage(man bash):

disown [-ar] [-h] [jobspec ...]

如果没有选项,则从活动作业表中删除每个jobspec。如果给出-h选项,则不会从表中删除每个jobspec,但会标记该作业,以便在shell接收到SIGHUP时不会向该作业发送SIGHUP。如果没有jobspec存在,并且未提供-a或-r选项,则使用当前作业。如果未提供jobspec,则-a选项表示删除或标记所有作业;没有jobspec参数的-r选项限制操作范围为正在运行的作业。返回值为0,除非jobspec未指定有效作业。

这意味着一个简单的

disown -a

将从任务表中删除所有作业并使它们成为 nohup。


12
disown -a 命令会移除所有作业。而简单的 disown 命令只会移除当前作业。正如答案中所述的手册页面。 - Rune Schjellerup Philosof

85

这些是很好的答案,我只想添加一个澄清:

你不能disown一个pid或进程,你需要disown一个job,这是一个重要的区别。

一个job是一个附加到shell的进程的概念,因此您必须将作业放入后台(而不是暂停它),然后disown它。

问题:

%  jobs
[1]  running java 
[2]  suspended vi
%  disown %1

请到http://www.quantprinciple.com/invest/index.php/docs/tipsandtricks/unix/jobcontrol/了解更详细的Unix作业控制讨论。


58

遗憾的是,disown 命令只适用于 bash 而不是所有的 shell。

某些 Unix 版本(例如 AIX 和 Solaris)在 nohup 命令本身上有一个选项,可以应用于正在运行的进程:

nohup -p pid

请参阅http://en.wikipedia.org/wiki/Nohup


2
仅适用于 AIXSolaris。 "Nohup 的 AIX 和 Solaris 版本具有 -p 选项,该选项修改正在运行的进程以忽略未来的 SIGHUP 信号。与 bash 中上述描述的 disown 内置不同,nohup -p 接受进程 ID。"。来源 - AlikElzin-kilaka

30
节点的回答非常好,但它留下了一个问题,如何获取stdout和stderr重定向。我在Unix&Linux上找到了一个解决方案,但它也不完整。我想合并这两个解决方案。这里是:

为了进行测试,我编写了一个名为loop.sh的小型bash脚本,在无限循环中打印自己的pid,并休眠一分钟。

$./loop.sh

现在以某种方式获取该进程的PID。通常使用ps -C loop.sh就足够了,但在我的情况下会打印出来。
现在我们可以切换到另一个终端(或按^Z键并在同一终端中)。现在gdb应该已经附加到这个进程上了。
$ gdb -p <PID>

这会停止脚本的运行。可以通过ps -f <PID>检查其状态,其中STAT字段为'T+'(或在^Z的情况下为'T'),这意味着(参见man ps(1))。
    T Stopped, either by a job control signal or because it is being traced
    + is in the foreground process group

(gdb) call close(1)
$1 = 0

Close(1)在成功时返回零。

(gdb) call open("loop.out", 01102, 0600)
$6 = 1

如果成功,Open(1)将返回新的文件描述符。

这个open等同于open(path, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)。可以使用O_WRONLY代替O_RDWR,但是/usr/sbin/lsof会在所有std* 文件处理程序(FD列)中显示“u”,这代表O_RDWR

我检查了/usr/include/bits/fcntl.h头文件中的值。

输出文件可以使用O_APPEND打开,就像nohup一样,但是man open(2)不建议这样做,因为可能会出现NFS问题。

如果返回-1,则call perror("")将打印错误消息。如果需要errno,请使用p errno gdb命令。

现在我们可以检查新重定向的文件。/usr/sbin/lsof -p <PID>会打印:

loop.sh <PID> truey    1u   REG   0,26        0 15008411 /home/truey/loop.out

如果需要,我们可以将stderr重定向到另一个文件,使用call close(2)call open(...)并再次使用不同的文件名。现在附加的bash必须被释放,然后我们可以退出gdb
(gdb) detach
Detaching from program: /bin/bash, process <PID>
(gdb) q

如果脚本被其他终端上的gdb停止,它会继续运行。我们可以切换回loop.sh的终端。现在它不会在屏幕上显示任何内容,但是正在运行并写入文件。我们必须将其置于后台。因此按下^Z
^Z
[1]+  Stopped                 ./loop.sh

现在我们的状态就好像一开始按下了^Z

现在我们可以检查工作的状态:

$ ps -f 24522
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID><PPID>  0 11:16 pts/36   S      0:00 /bin/bash ./loop.sh
$ jobs
[1]+  Stopped                 ./loop.sh

进程应该在后台运行并与终端分离。 jobs 命令输出中方括号中的数字标识了 bash 中的作业。我们可以在以下内置的 bash 命令中使用 '%' 符号加上作业编号:

$ bg %1
[1]+ ./loop.sh &
$ disown -h %1
$ ps -f <PID>
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID><PPID>  0 11:16 pts/36   S      0:00 /bin/bash ./loop.sh

现在我们可以退出调用的bash。该进程继续在后台运行。如果我们退出,它的PPID变为1(init(1)进程),控制终端变为未知。

$ ps -f <PID>
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID>     1  0 11:16 ?        S      0:00 /bin/bash ./loop.sh
$ /usr/bin/lsof -p <PID>
...
loop.sh <PID> truey    0u   CHR 136,36                38 /dev/pts/36 (deleted)
loop.sh <PID> truey    1u   REG   0,26     1127 15008411 /home/truey/loop.out
loop.sh <PID> truey    2u   CHR 136,36                38 /dev/pts/36 (deleted)

评论

可以通过创建一个包含命令的文件(例如loop.gdb)来自动化gdb操作,并运行gdb -q -x loop.gdb -p <PID>。我的loop.gdb文件如下:

call close(1)
call open("loop.out", 01102, 0600)
# call close(2)
# call open("loop.err", 01102, 0600)
detach
quit

或者可以使用以下的一行代码替代:
gdb -q -ex 'call close(1)' -ex 'call open("loop.out", 01102, 0600)' -ex detach -ex quit -p <PID>

我希望您能理解这个解决方案,以下是一个相对完整的描述。

1
确实非常有用,对于简单的情况可能会很有效。但要注意,更复杂的情况可能会失败得很惨。我今天就遇到了这样的情况:我的进程生成了另一个进程来输出(可能是到stderr),但将stdout连接到其主进程进行通信。重定向主进程的FD是无效的,因为子进程已经继承了stderr,并且关闭子进程的stdout会导致等待在管道另一端的主进程失败。X-| 在尝试此操作之前最好充分了解您的进程。 - cmaster - reinstate monica
1
@cmaster 你可以使用 lsof(文件句柄的名称为 pipe,而不是像 /dev/pts/1)或者 ls -l /proc/<PID>/fd/<fd> 来检查句柄是否被重定向。此外,子进程仍然可能有未重定向的输出,这些输出应该被重定向到一个文件中。 - TrueY
如何将一个已分离的进程重新连接到终端,这里有最佳答案!非常感谢! - shaheen g

20

简单易行的步骤

  1. Ctrl + Z ----------> 挂起进程
  2. bg --------------> 恢复并在后台运行
  3. disown %1 -------------> 仅在需要从终端分离时使用

1
小心。这种方式的进程仍然绑定在终端上,如果终端关闭,它将终止。 - Fernando Fabreti
1
disown -h %1 似乎可以避免 SIGHUP(完全将进程与终端分离)。 - Fernando Fabreti

6

将正在运行的进程发送到nohup (http://en.wikipedia.org/wiki/Nohup)。

nohup -p pid,对我来说没有用。

然后我尝试了以下命令,它非常好用:

  1. 运行SOMECOMMAND,比如 /usr/bin/python /vol/scripts/python_scripts/retention_all_properties.py 1

  2. Ctrl+Z 暂停程序并返回到shell。

  3. bg 在后台运行。

  4. disown -h避免在关闭终端时终止该进程。

  5. 输入exit退出shell,因为现在你可以自由地进行操作了,该操作将在后台自己运行,因此它不会与shell绑定。

该过程等同于运行nohup SOMECOMMAND


5
  1. ctrl + z - 这将暂停任务(不会取消!)
  2. bg - 这将把任务放在后台运行并返回。
  3. disown -a - 这将切断与任务的所有联系(因此您可以关闭终端,它仍将运行)

这些简单的步骤将使您能够关闭终端而保持进程正在运行。

它不会使用nohup(根据我对您问题的理解,您在这里不需要它)。


1
我认为 disown -a 的行为是与所有作业断开连接。 但是,它不会关闭 stdin/stdout 管道,这意味着进程仍然会尝试从终端写入(/读取)。 - Kelthar

3
在我的AIX系统上,我尝试了
nohup -p  processid>

这个方法很有效。即使关闭终端窗口,它也可以继续运行我的进程。由于我们的默认shell是ksh,因此bgdisown命令无法使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接