【问题标题】:How do I daemonize an arbitrary script in unix?如何在 unix 中守护任意脚本?
【发布时间】:2026-01-04 06:00:01
【问题描述】:

我想要一个可以将任意通用脚本或命令转换为daemon 的守护程序。

我想处理两种常见的情况:

  1. 我有一个应该永远运行的脚本。如果它死了(或在重新启动时),请重新启动它。不要让两个副本同时运行(检测一个副本是否已经在运行,在这种情况下不要启动它)。

  2. 我有一个简单的脚本或命令行命令,我想永远重复执行(运行之间有短暂的停顿)。同样,不要让脚本的两个副本同时运行。

当然,在案例 2 中围绕脚本编写“while(true)”循环然后为案例 1 应用解决方案是微不足道的,但更通用的解决方案将直接解决案例 2,因为这适用于情况 1 也是(如果脚本不打算死掉,你可能只需要更短的暂停或没有暂停(当然,如果脚本真的确实永远不会死,那么暂停实际上并不重要)) .

请注意,该解决方案不应涉及,例如,向现有脚本添加文件锁定代码或 PID 记录。

更具体地说,我想要一个可以运行的程序“守护进程”

% daemonize myscript arg1 arg2

或者,例如,

% daemonize 'echo `date` >> /tmp/times.txt'

这将保留不断增长的附加到 times.txt 的日期列表。 (请注意,如果 daemonize 的参数是一个像上面的情况 1 一样永远运行的脚本,那么 daemonize 仍然会做正确的事情,在必要时重新启动它。)然后我可以在我的 .login 中放置一个类似上面的命令和/或每小时或每分钟 cron 它(取决于我对它意外死亡的担心程度)。

注意:daemonize 脚本需要记住它正在守护的命令字符串,这样如果再次守护相同的命令字符串,它就不会启动第二个副本。

此外,理想情况下,该解决方案应该适用于 OS X 和 linux,但欢迎使用其中一种或另一种的解决方案。

编辑:如果你必须用sudo daemonize myscript myargs 调用它也没关系。

(如果我认为这一切都错了,或者有快速而肮脏的部分解决方案,我也很想听听。)


PS:如果有用的话,here's 一个类似的特定于 python 的问题。

this 对类似问题的回答似乎是一个有用的成语,可以快速而肮脏地妖魔化任意脚本:

【问题讨论】:

标签: perl unix scripting sysadmin daemon


【解决方案1】:

您可以使用 nohup 和 & 运算符来守护 Unix 中的任何可执行文件:

nohup yourScript.sh script args&

nohup 命令允许您在不杀死脚本的情况下关闭您的 shell 会话,而 & 将您的脚本置于后台,以便您获得 shell 提示以继续您的会话。唯一的小问题是标准输出和标准错误都被发送到./nohup.out,所以如果你在这个庄园中启动几个脚本,它们的输出将交织在一起。更好的命令是:

nohup yourScript.sh script args >script.out 2>script.error&

这会将标准输出发送到您选择的文件,并将标准错误发送到您选择的其他文件。如果您只想对标准输出和标准错误使用一个文件,您可以这样做:

nohup yourScript.sh script args >script.out 2>&1 &

2>&1 告诉 shell 将标准错误(文件描述符 2)重定向到与标准输出(文件描述符 1)相同的文件。

要只运行一次命令并在它死后重新启动它,您可以使用此脚本:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

第一个参数是要使用的 pid 文件的名称。第二个参数是命令。所有其他参数都是命令的参数。

如果您将此脚本命名为restart.sh,您会这样称呼它:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

【讨论】:

  • 太棒了;谢谢。我想知道它是否也应该有一个延迟重启的选项。或者最好将它与它结合使用:*.com/questions/555116/…
  • 这只处理 SIGHUP,还有其他(通常)致命的信号需要处理。
  • 另一种改进此脚本的方法可能是让它自己找到一个放置 $PIDFILE 的好位置,而不是要求将其指定为 arg。它甚至不会自行清理! (使用trap EXIT 应该很简单)
  • 另外,考虑&lt;test 中的使用是ASCII 比较而不是整数比较。它可能仍然有效,但可能会导致错误。
  • 我已经发布了我对这个脚本的修复here
【解决方案2】:

对于冗长的答案,我深表歉意(请参阅 cmets,了解我的答案如何符合规范)。我试图做到全面,所以你有尽可能好的一面。 :-)

如果您能够安装程序(具有 root 访问权限),并且愿意一次性完成设置脚本以执行守护程序(即,比简单地指定要在命令行,但每个服务只需要执行一次),我有一种更强大的方法。

它涉及使用daemontools。这篇文章的其余部分描述了如何使用 daemontools 设置服务。

初始设置

  1. 按照How to install daemontools 中的说明进行操作。一些发行版(例如 Debian、Ubuntu)已经有它的软件包,所以就使用它吧。
  2. 创建一个名为/service 的目录。安装程序应该已经这样做了,但只需验证,或者手动安装。如果你不喜欢这个位置,你可以在你的 svscanboot 脚本中更改它,尽管大多数 daemontools 用户习惯使用 /service,如果你不使用它会感到困惑。
  3. 如果您使用的是 Ubuntu 或其他不使用标准 init(即不使用 /etc/inittab)的发行版,则需要使用预安装的 inittab 作为安排的基础svscanboot 将由 init 调用。这并不难,但您需要知道如何配置您的操作系统使用的initsvscanboot 是一个调用svscan 的脚本,主要工作是寻找服务;它是从init 调用的,所以init 将安排在它因任何原因死亡时重新启动它。

按服务设置

  1. 每个服务都需要一个服务目录,该目录存储有关服务的内务管理信息。您还可以创建一个位置来存放这些服务目录,以便它们都在一个地方;通常我使用/var/lib/svscan,但任何新位置都可以。
  2. 我通常使用a script来设置服务目录,以节省大量手动重复工作。例如,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
    

    some-service-name 是您要为服务提供的名称,user 是运行该服务的用户,loguser 是运行记录器的用户。 (稍微解释一下日志记录。)

  3. 您的服务必须在前台运行。如果您的程序默认为背景,但可以选择禁用它,请执行此操作。如果您的程序后台无法禁用它,请阅读fghack,尽管这是一个权衡:您无法再使用svc 控制程序。
  4. 编辑run 脚本以确保它按照您的意愿运行。如果您希望您的服务经常退出,您可能需要在顶部拨打sleep 电话。
  5. 一切设置正确后,在/service 中创建一个指向您的服务目录的符号链接。 (不要将服务目录直接放在/service 中;这会使从svscan 的手表中删除服务变得更加困难。)

记录

  1. daemontools 的日志记录方式是让服务将日志消息写入标准输出(或标准错误,如果您使用的是由mkservice 生成的脚本); svscan 负责将日志消息发送到日志服务。
  2. 日志服务从标准输入中获取日志消息。 mkservice 生成的日志服务脚本将在log/main 目录中创建自动轮换的、带有时间戳的日志文件。当前日志文件名为current
  3. 日志服务可以独立于主服务启动和停止。
  4. 通过tai64nlocal 传送日志文件会将时间戳转换为人类可读的格式。 (TAI64N 是一个以纳秒为单位的 64 位原子时间戳。)

控制服务

  1. 使用svstat 获取服务的状态。请注意,日志服务是独立的,并且有自己的状态。
  2. 您可以使用svc 控制您的服务(启动、停止、重新启动等)。例如,要重新启动您的服务,请使用svc -t /service/some-service-name-t 表示“发送SIGTERM”。
  3. 其他可用信号包括-h (SIGHUP)、-a (SIGALRM)、-1 (SIGUSR1)、-2 (SIGUSR2) 和@9876543732@ (SIGKILL) @)。
  4. 要关闭服务,请使用-d。您还可以通过在服务目录中创建一个名为 down 的文件来防止服务在启动时自动启动。
  5. 要启动服务,请使用-u。除非您之前已将其关闭(或将其设置为不自动启动),否则这不是必需的。
  6. 要让主管退出,请使用-x;通常与-d 一起使用也可以终止服务。这是允许删除服务的常用方法,但您必须先从/service 取消链接服务,否则svscan 将重新启动主管。 此外,如果您使用日志记录服务 (mkservice -l) 创建了服务,请记住在删除服务目录之前还要退出日志记录主管(例如,svc -dx /var/lib/svscan/some-service-name/log)。

总结

优点:

  1. daemontools 提供了一种创建和管理服务的防弹方法。我将它用于我的服务器,我强烈推荐它。
  2. 其日志记录系统非常强大,服务自动重启工具也是如此。
  3. 因为它使用您编写/调整的 shell 脚本启动服务,所以您可以根据需要定制服务。
  4. 强大的服务控制工具:您可以向服务发送几乎任何信号,并且可以可靠地启动和关闭服务。
  5. 保证您的服务有一个干净的执行环境:它们将在与init 提供的环境、进程限制等相同的环境下执行。

缺点:

  1. 每个服务都需要一些设置。值得庆幸的是,每次服务只需执行一次。
  2. 必须将服务设置为在前台运行。此外,为了获得最佳结果,应将它们设置为记录到标准输出/标准错误,而不是 syslog 或其他文件。
  3. 如果您不熟悉 daemontools 的工作方式,学习曲线会很陡峭。您必须使用svc 重新启动服务,并且不能直接运行运行脚本(因为它们将不受主管的控制)。
  4. 大量的内务管理文件和大量的内务处理流程。每个服务都需要自己的服务目录,每个服务使用一个主管进程在服务死亡时自动重启服务。 (如果您有许多服务,您将在进程表中看到 lotssupervise 进程。)

总的来说,我认为 daemontools 是满足您需求的优秀系统。我欢迎任何关于如何设置和维护它的问题。

【讨论】:

  • 我的回答如何确定规范: 1. 您必须设置服务,因此只要您不设置重复项(并且只要您的服务本身没有背景),就没有重复项会发生。 2. supervise,主管,负责重启任何退出的服务。它在重新启动之间等待一秒钟;如果时间不够,请在服务运行脚本的顶部进入睡眠状态。
  • 2a。 supervise 本身由 svscan 支持,因此如果主管死了,它将重新启动。 2b。 svscaninit 支持,它会根据需要自动重启 svscan。 2c。如果您的init 因任何原因而死,无论如何您都会被搞砸。 :-P
  • 为了回答有关家务的其他问题,daemontools 系统不使用 PID 文件,因为它们可能会过时。相反,所有流程信息都由支持给定服务的主管保存。主管在服务目录中维护了一堆文件(和 FIFO),svstatsvc 等工具可以使用这些文件。
  • 我们应该在 SO 和一般网络中发布更多这样的帖子。不仅仅是一个关于如何达到预期效果的食谱,而是一个不厌其烦地解释食谱的食谱。为什么我不能多次投票? :|
【解决方案3】:

你应该看看daemonize。它允许检测第二个副本(但它使用文件锁定机制)。它还适用于不同的 UNIX 和 Linux 发行版。

如果您需要将应用程序作为守护进程自动启动,则需要创建适当的 init-script。

您可以使用以下模板:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

【讨论】:

  • 看起来是正确答案的候选者。特别是考虑到它的“单实例检查”。
  • 这可能是最好的答案——我不确定——但如果你认为是,你能否解释一下为什么我在问题中给出的规范是错误的?
  • 我不喜欢停止部分的killproc:如果你有一个进程运行javakillproc 将导致所有其他Java 进程也被杀死.
  • 从 /etc/rc.d/init.d/functions,daemonize 只是从一个新的 shell 启动二进制文件: $cgroup $nice /bin/bash -c $corelimit &gt;/dev/null 2&gt;&amp;1 ; $* 所以我怀疑它会继续守护任何东西......
  • 我知道这是旧的,但对于以后发现它的任何人......这是正确的。 /etc/init.d/functions 中定义的“守护进程”实际上 not 为您守护进程。它只是做 cgroups、检查进程是否已经在运行、设置用户、设置 nice 和 ulimit 值等的包装器。它为你守护进程。那仍然是你自己的工作。 :)
【解决方案4】:

我想你可能想试试start-stop-daemon(8)。在任何 Linux 发行版中查看 /etc/init.d 中的脚本以获取示例。它可以通过调用的命令行或 PID 文件找到已启动的进程,因此它符合您的所有要求,除了作为脚本的看门狗。但您始终可以启动另一个守护程序看门狗脚本,在必要时重新启动您的脚本。

【讨论】:

  • Fedora 中还没有启动-停止-守护进程,因此依赖于它的脚本是不可移植的。见:fedoraproject.org/wiki/Features/start-stop-daemon
  • 对于 OSX 用户的提醒:那里也没有 start-stop-daemon(从 10.9 开始)。
  • @mklement0 嗯...近 5 年发生了很多变化。
  • 我的,时间过得真快。不过,start-stop-daemon 仍然活跃在 Linux 上;但在阅读答案*.com/a/525406/45375 后,我意识到OSX 做了自己的事情:launchd
【解决方案5】:

作为已经提到的daemonizedaemontools 的替代方案,还有libslack 包的daemon 命令。

daemon 非常可配置,并且确实关心所有繁琐的守护进程,例如自动重启、日志记录或 pidfile 处理。

【讨论】:

    【解决方案6】:

    如果你专门使用 OS X,我建议你看看 launchd 是如何工作的。它将自动检查以确保您的脚本正在运行,并在必要时重新启动它。它还包括各种调度功能等。它应该同时满足要求1和2。

    至于确保您的脚本只能运行一个副本,您需要使用 PID 文件。通常我将一个文件写入 /var/run/.pid ,其中包含当前正在运行的实例的 PID。如果程序运行时文件存在,它会检查文件中的 PID 是否实际运行(程序可能已经崩溃或忘记删除 PID 文件)。如果是,则中止。如果没有,开始运行并覆盖 PID 文件。

    【讨论】:

      【解决方案7】:

      Daemontools (http://cr.yp.to/daemontools.html) 是一组用于执行此操作的非常核心的实用程序,由 dj bernstein 编写。我已经成功地使用了它。令人讨厌的部分是,当您运行它们时,没有任何脚本返回任何可见的结果 - 只是不可见的返回码。但是一旦运行起来,它就是防弹的。

      【讨论】:

      • 是的,我也打算写一个使用 daemontools 的条目。我会写我自己的帖子,因为我希望我的答案更加全面,并希望以这种方式获得赏金。走着瞧。 :-)
      【解决方案8】:

      首先从http://code.activestate.com/recipes/278731/获取createDaemon()

      然后是主要代码:

      import subprocess
      import sys
      import time
      
      createDaemon()
      
      while True:
          subprocess.call(" ".join(sys.argv[1:]),shell=True)
          time.sleep(10)
      

      【讨论】:

      • 哦,谢谢!想让它更通用一点,以便您可以执行“daemonize foo arg1 arg2”和“daemonize 'foo arg1 arg2'”?
      • 好的,它现在将加入参数 - 但是如果您想在参数中包含空格,则必须更改它。
      • 谢谢道格拉斯!但是有一个很大的缺陷:运行两次“daemonize foo”会启动两个 foo 副本。
      • 您可以添加一些 PID 记录代码,但最好只运行一次脚本...
      • 我认为这是整个“守护进程”包装概念的基础。 (例如,我可以每小时或每分钟 cron 来确保它一直在运行。)我想错了吗? createDaemon 是否已经以某种方式保证了这一点?重启后呢?
      【解决方案9】:

      这是一个完整的工作版本,您可以将其复制到一个空目录并试用(在安装 CPAN 依赖项后,它们是 Getopt::LongFile::SpecFile::PidIPC::System::Simple --所有这些都非常标准,强烈推荐给任何黑客:您可以使用cpan &lt;modulename&gt; &lt;modulename&gt; ... 一次性安装它们。


      keepAlive.pl:

      #!/usr/bin/perl
      
      # Usage:
      # 1. put this in your crontab, to run every minute:
      #     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
      # 2. put this code somewhere near the beginning of your script,
      #    where $pidfile is the same value as used in the cron job above:
      #     use File::Pid;
      #     File::Pid->new({file => $pidfile})->write;
      
      # if you want to stop your program from restarting, you must first disable the
      # cron job, then manually stop your script. There is no need to clean up the
      # pidfile; it will be cleaned up automatically when you next call
      # keepAlive.pl.
      
      use strict;
      use warnings;
      
      use Getopt::Long;
      use File::Spec;
      use File::Pid;
      use IPC::System::Simple qw(system);
      
      my ($pid_file, $command);
      GetOptions("pidfile=s"   => \$pid_file,
                 "command=s"   => \$command)
          or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;
      
      my @arguments = @ARGV;
      
      # check if process is still running
      my $pid_obj = File::Pid->new({file => $pid_file});
      
      if ($pid_obj->running())
      {
          # process is still running; nothing to do!
          exit 0;
      }
      
      # no? restart it
      print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";
      
      system($command, @arguments);
      

      example.pl:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      use File::Pid;
      File::Pid->new({file => "pidfile"})->write;
      
      print "$0 got arguments: @ARGV\n";
      

      现在您可以使用./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3 调用上面的示例,并且将创建文件pidfile,您将看到输出:

      Pid <random number here> no longer running; restarting ./example.pl 1 2 3
      ./example.pl got arguments: 1 2 3
      

      【讨论】:

      • 如果我理解正确的话,我相信这并不完全符合规范。在您的解决方案中(谢谢,顺便说一句!)必须修改要守护进程的程序以将其 PID 写入 PID 文件。我希望有一个可以守护任意脚本的实用程序。
      • @dreeves: 是的,但是有两种方法可以解决这个问题:1. keepAlive.pl 调用的脚本(例如 example.pl)可能只是一个执行真实程序的包装器,或者 2. keepAlive.pl 可以解析活动系统进程表(使用 CPAN 的 Proc::ProcessTable)以尝试找到相关进程及其 pid)。
      【解决方案10】:

      您也可以试试Monit。 Monit 是一项监视和报告其他服务的服务。虽然它主要用作通知(通过电子邮件和短信)有关运行时问题的一种方式,但它也可以执行这里大多数其他建议所提倡的。它可以自动(重新)启动和停止程序、发送电子邮件、启动其他脚本以及维护您可以获取的输出日志。此外,我发现它很容易安装和维护,因为它有可靠的文档。

      【讨论】:

        【解决方案11】:

        你可以试试immortal 它是一个 *nix 跨平台(与操作系统无关)主管。

        在 macOS 上快速尝试:

        brew install immortal
        

        如果您从端口使用 FreeBSD 或使用 pkg:

        pkg install immortal
        

        对于Linux,通过下载预编译的二进制文件或从源代码:https://immortal.run/source/

        你可以像这样使用它:

        immortal -l /var/log/date.log date
        

        或通过configuration YAML 文件为您提供更多选项,例如:

        cmd: date
        log:
            file: /var/log/date.log
            age: 86400 # seconds
            num: 7     # int
            size: 1    # MegaBytes
            timestamp: true # will add timesamp to log
        

        如果您还想将标准错误输出保存在单独的文件中,您可以使用以下内容:

        cmd: date
        log:
            file: /var/log/date.log
            age: 86400 # seconds
            num: 7     # int
            size: 1    # MegaBytes
        stderr:
            file: /var/log/date-error.log
            age: 86400 # seconds
            num: 7     # int
            size: 1    # MegaBytes
            timestamp: true # will add timesamp to log
        

        【讨论】:

          【解决方案12】:

          我对@9​​87654321@做了一系列改进。

          1. 此脚本中的标准输出完全由来自其子级的标准输出组成,除非它因检测到该命令已在运行而退出
          2. 在其 pidfile 终止后清理
          3. 可选的可配置超时期限(接受任何正数参数,发送至sleep
          4. -h 上的使用提示
          5. 任意命令执行,而不是单个命令执行。最后一个 arg 或剩余的 args(如果有多个最后一个 args)被发送到eval,因此您可以将任何类型的 shell 脚本作为字符串发送到该脚本作为最后一个 arg(或尾随 args)守护进程
          6. 使用-lt 而不是&lt; 完成的参数计数比较

          这是脚本:

          #!/bin/sh
          
          # this script builds a mini-daemon, which isn't a real daemon because it
          # should die when the owning terminal dies, but what makes it useful is
          # that it will restart the command given to it when it completes, with a
          # configurable timeout period elapsing before doing so.
          
          if [ "$1" = '-h' ]; then
              echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
              exit
          fi
          
          if [ $# -lt 2 ]; then
              echo "No command given."
              exit
          fi
          
          PIDFILE=$1
          shift
          
          TIMEOUT=1
          if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
                  TIMEOUT=$1
                  [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
                  shift
          fi
          
          echo "Checking pid in file ${PIDFILE}." >&2
          
          #Check to see if process running.
          if [ -f "$PIDFILE" ]; then
              PID=$(< $PIDFILE)
              if [ $? = 0 ]; then
                  ps -p $PID >/dev/null 2>&1
                  if [ $? = 0 ]; then
                      echo "This script is (probably) already running as PID ${PID}."
                      exit
                  fi
              fi
          fi
          
          # Write our pid to file.
          echo $$ >$PIDFILE
          
          cleanup() {
                  rm $PIDFILE
          }
          trap cleanup EXIT
          
          # Run command until we're killed.
          while true; do
              eval "$@"
              echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
              sleep $TIMEOUT
          done
          

          用法:

          $ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
          Checking pid in file pidfilefortesting.
          azzzcd
          I am 79281 and my child has exited; restart in 0.5s
          azzzcd
          I am 79281 and my child has exited; restart in 0.5s
          azzzcd
          I am 79281 and my child has exited; restart in 0.5s
          ^C
          
          $ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
          azzzcd
          azzzcd
          azzzcd
          ^C
          

          请注意,如果您从不同的目录运行此脚本,它可能会使用不同的 pidfile,并且不会检测到任何现有的正在运行的实例。由于它旨在运行和重新启动通过参数提供的临时命令,因此无法知道某事是否已经启动,因为谁能说它是否是相同的命令?为了改进这种只运行单个实例的强制执行,需要针对具体情况的解决方案。

          此外,要使其充当适当的守护程序,您必须(至少)使用 nohup 作为其他答案提到的。我没有努力为过程可能收到的信号提供任何弹性。

          还有一点需要注意的是,杀死这个脚本(如果它是从另一个被杀死的脚本调用的,或者有一个信号)可能无法成功杀死孩子,特别是如果孩子还另一个 脚本。我不确定这是为什么,但这似乎与eval 的工作方式有关,这对我来说很神秘。因此,谨慎的做法是将该行替换为仅接受单个命令的内容,例如另一个答案。

          【讨论】:

            【解决方案13】:

            还有一个非常简单的双叉 + setsid 方法可以将任何脚本与其父进程分离

            ( setsid my-regular-script arg [arg ...] 1>stdout.log 2>stderr.log & )
            

            setsid 是标准util-linux 包的一部分,自诞生之日起就与Linux 一起使用。这在我知道的任何POSIX 兼容的 shell 中启动时都有效。

            另一种基于双叉的方法甚至不需要任何额外的可执行文件或包,并且完全依赖于基于 POSIX 的 shell

            ( my-regular-script arg [arg ...] 1>stdout.log 2>stderr.log & ) &
            

            当父进程离开阶段时,它也可以成为孤儿

            【讨论】: