【问题标题】:Shell script that can check if it was backgrounded at invocation可以在调用时检查它是否在后台运行的 Shell 脚本
【发布时间】:2010-08-31 14:30:54
【问题描述】:

我编写了一个依赖于其他服务器响应的脚本(使用wget 来提取数据),并且我希望它始终毫无疑问地在后台运行。我知道一种解决方案是编写一个包装器脚本,该脚本将调用我的脚本并附加&,但我想避免这种混乱。

有没有办法让 bash(或 zsh)脚本确定它是否被称为 ./foo.sh &,如果没有,则退出并重新启动它本身?

【问题讨论】:

    标签: bash shell zsh


    【解决方案1】:

    后台进程的定义(我认为)是它有一个控制终端,但它不属于该终端的前台进程组。我不认为任何 shell,甚至 zsh,都无法通过内置函数让您访问该信息。

    在 Linux(可能还有其他 unices)上,当进程是其终端前台进程组的一部分时,psSTAT 列包含 +。因此,对您的问题的字面回答是,您可以将脚本的内容放在 main 函数中并使用以下命令调用它:

    case $(ps -o stat= -p $$) in
      *+*) main "$@" &;;
      *) main "$@";;
    esac
    

    但你还是可以运行main "$@" &。在 Unix 上,fork 很便宜。

    但是,我强烈建议您不要按照您的建议去做。这使得某人无法运行您的脚本并在之后执行其他操作——人们期望能够编写your_script; my_postprocessingyour_script && my_postprocessing,但是分叉​​脚本的主要任务使这成为不可能。考虑到在调用脚本时偶尔会节省一个字符,因此以这种方式显着降低脚本的用处是不值得的。

    如果您真的是想让脚本在后台运行以便用户可以关闭他的终端,那么您需要做更多的工作——您需要daemonize脚本,这不仅包括后台,还包括关闭所有打开终端的文件描述符,使进程成为会话领导者等等。我认为这需要将您的脚本拆分为一个守护程序包装脚本和一个主脚本。但是,除非明确停止,否则通常不会终止程序,这不是您描述的行为。

    【讨论】:

    • 您好 Gilles,感谢您的详细回复。您提供的解决方案与 ZyX 一起使用,但我接受了他的解决方案,因为它更加独立并且不依赖 ps 或任何分支。作为记录,该脚本仅供我自己使用,不会被其他用户触及,并且位于 /etc/init.d 中,因此不会关闭终端。谢谢 =) +1
    【解决方案2】:

    我不知道该怎么做,但你可以在父脚本中设置变量并在子脚本中检查它:

    if [[ -z "$_BACKGROUNDED" ]] ; then
        _BACKGROUNDED=1 exec "$0" "$@" & exit
    fi
    # Put code here
    

    适用于 bash 和 zsh。

    【讨论】:

    • 我会接受这个答案,它似乎做我想做的事,除了在你的解决方案中“endif”应该是“fi”;-) 非常感谢。
    • 额外的想法:我意识到一个挑剔的人可能会争辩说这个snipplet会重新启动脚本,即使是在调用的背景下,但我不需要检查这个额外的复杂性。
    • 请注意,exec $0 $@ 会使您的论点一团糟。为了安全起见,引用是必需的:exec "$0" "$@" -- 否则你会得到 ./yourprogram "argument one" "argument two"./yourprogram "argument" "one" "argument" "two" 运行相同 -- 那是在我们开始谈论通配符之前。
    • @CharlesDuffy 抱歉,当时太习惯 zsh,但不知道一些极端情况。由于您所说的原因,您不需要在 zsh 中使用额外的引号(假设默认的 zsh 选项),除了只是为了让每个人的生活更加困难,不引用 $@ 将删除所有等于空字符串的参数,而不是 $0 如果 @987654328 引用@ 扩展为空字符串将表现得好像它不存在一样。 IE。通配符或空格或其他任何问题都没有问题,但由于空字符串,您仍然需要引用。
    【解决方案3】:

    如果您在后台,“tty”命令会说“不是 tty”,如果您在前台,则给出控制终端名称(例如 /dev/pts/1)。一个简单的判断方法。

    【讨论】:

    • 这对我来说似乎不是这样:调用 tty 的 shell 脚本会为我返回一个终端,无论我是在前台调用脚本还是将 & 放在结束。
    【解决方案4】:

    请记住,您不能(或不建议)编辑正在运行的脚本。 This question and the answers 提供解决方法。

    【讨论】:

      【解决方案5】:

      我很久以前不写shell脚本了,但我可以给你一个很好的想法(我希望)。您可以检查 $$ 的值(这是进程的 PID)并与命令“jobs -l”的输出进行比较。最后一条命令将返回所有后台进程(作业)的 PI​​D,如果 $$ 的值包含在“jobs -l”的结果中,这意味着当前脚本正在后台运行。

      【讨论】:

      • jobs -l 无法访问父进程的作业列表,请检查zsh -c 'jobs -l; echo $$; sleep 1s' & 的输出:除了$$,它不会回显任何内容。
      • 我写了一个小shell脚本来测试我说的话,我写了jobs -l和echo $$。当我在 fg 中运行我的脚本时,当然 jobs -l 没有返回任何内容,但是当我在 bg 中运行时,它返回的结果与 $$ 相同...我正在使用 bash
      • 如何运行脚本?我在 bash 和 zsh 中都没有看到这种行为。
      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2014-07-18
      • 2020-07-10
      • 2011-08-27
      • 1970-01-01
      • 2014-08-21
      • 1970-01-01
      相关资源
      最近更新 更多