关于shell:如何在nohup下放置已经运行的进程?

How do I put an already-running process under nohup?

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

如何将其置于nohup下(即,即使关闭终端,如何使其继续运行?)


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

  • ctrl+zabbd停止(暂停)程序并返回shell。
  • bg在后台运行。
  • disown -h [job-spec],其中〔job spec〕是第一个运行作业的作业编号(如%1;使用jobs命令查找您的编号,以便在终端关闭时不会终止作业。

  • 假设由于某种原因CtrL+Z也不工作,转到另一个终端,找到进程ID(使用ps并运行:

    1
    2
    kill -SIGSTOP PID
    kill -SIGCONT PID

    SIGSTOP将暂停进程,SIGCONT将在后台恢复进程。所以现在,关闭两个终端不会停止您的进程。


    将正在运行的作业与shell(=makeit nohup)分开的命令是disown和基本shell命令。

    从bash手册页(man bash):

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

    Without options, each jobspec is removed from the table of active jobs. 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 job if the shell receives a SIGHUP. 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
    disown -a

    将从作业表中删除所有作业并使其不显示


    以上是很好的答案,我只是想补充一点:

    你不能用一个pid或进程来处理disown,你用一个作业来处理,这是一个重要的区别。

    作业是一个附加到shell的流程概念,因此您必须将作业抛到后台(而不是挂起它),然后再拒绝它。

    问题:

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

    请参阅http://www.quantprinciple.com/invest/index.php/docs/tipsandtricks/unix/jobcontrol/有关Unix作业控制的更详细的讨论。


    不幸的是,disown是bash特有的,并非在所有shell中都可用。

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

    1
    nohup -p pid

    参见http://en.wikipedia.org/wiki/nohup


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

    为了进行测试,我编写了一个名为loop.sh的小型bash脚本,它以无限循环的形式打印了一分钟睡眠的PID。

    1
    $./loop.sh

    现在以某种方式获取这个过程的PID。通常情况下,ps -C loop.sh已经足够好了,但它是在我的情况下打印的。

    现在我们可以切换到另一个终端(或按^Z键在同一个终端)。现在,gdb应该附在这个过程中。

    1
    $ gdb -p <PID>

    这将停止脚本(如果正在运行)。它的状态可以通过ps -f 来检查,其中STAT字段是't+'(或在'z't'的情况下),这意味着(man ps(1))。

    1
    2
    3
    4
    5
        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)成功时返回零。

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

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

    此开度与open(path, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)相等。可以用O_RDWR代替O_WRONLY,但/usr/sbin/lsof表示所有std*文件处理程序(FD列)的"u",即O_RDWR

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

    输出文件可以用O_APPEND打开,正如nohup所做的那样,但由于可能存在NFS问题,man open(2)不建议这样做。

    如果我们得到-1作为返回值,那么call perror("")将打印错误消息。如果我们需要errno,使用p errnogdb-comand。

    现在我们可以检查新重定向的文件。/usr/sbin/lsof -p 印刷品:

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

    如果需要,我们可以将stderr重定向到另一个文件,如果我们想再次使用call close(2)call open(...),使用不同的文件名。

    现在附件bash必须发布,我们可以退出gdb了:

    1
    2
    3
    (gdb) detach
    Detaching from program: /bin/bash, process <PID>
    (gdb) q

    如果脚本被其他终端的gdb停止,它将继续运行。我们可以切换回loop.sh的终端。现在它不向屏幕写入任何内容,而是运行并写入文件。我们必须把它放到背景中。所以按^Z

    1
    2
    ^Z
    [1]+  Stopped                 ./loop.sh

    (现在我们的状态和一开始按^Z时一样。)

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

    1
    2
    3
    4
    5
    $ 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命令中使用,在作业编号前应用"%"符号:

    1
    2
    3
    4
    5
    6
    $ 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

    现在我们可以不再打电话了。进程继续在后台运行。如果我们退出它的ppid变为1(init(1)进程),控制终端将变为未知。

    1
    2
    3
    4
    5
    6
    7
    8
    $ 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)

    评论

    gdb工具可以自动创建包含命令的文件(例如loop.gdb)并运行gdb -q -x loop.gdb -p 。我的loop.gdb如下所示:

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

    或者可以使用以下一个内衬:

    1
    gdb -q -ex 'call close(1)' -ex 'call open("loop.out", 01102, 0600)' -ex detach -ex quit -p <PID>

    我希望这是对解决方案的一个相当完整的描述。


    将运行进程发送到nohup(http://en.wikipedia.org/wiki/nohup)

    以东十一〔十二〕号,对我不起作用。

    然后我试了下面的命令,效果很好

  • 运行一些命令,比如说/usr/bin/python /vol/scripts/python_scripts/retention_all_properties.py 1

  • ctrl+zabbd停止(暂停)程序并返回shell。

  • bg在后台运行。

  • disown -h以便终端关闭时不会终止进程。

  • 键入exit以退出shell,因为现在可以开始了,因为该操作将在后台运行,在其自身进程中运行,因此它不与shell绑定。

  • 这个过程相当于运行nohup SOMECOMMAND


  • ctrl+zabbkbd-这将暂停作业(不会取消!)
  • bg—这将把作业置于后台,并在运行过程中返回。
  • disown -a—这将切断所有与作业的连接(这样您可以关闭终端,它仍将运行)
  • 这些简单的步骤将允许您在保持进程运行的同时关闭终端。

    这不会影响到nohup(根据我对你的问题的理解,你在这里不需要它)。


    在我的AIX系统上,我尝试了

    1
    nohup -p  processid>

    这很管用。它继续运行我的进程,即使在关闭终端窗口之后。我们使用ksh作为默认shell,因此bgdisown命令不起作用。


    在tcshell中,这在UbuntuLinux上对我很有用。

  • ctrlzakbd暂停

  • bg在后台运行

  • jobs获取作业编号

  • nohup %n,其中n是作业编号。