如何设置shell脚本的进程组

时间:2011-07-01 14:59:50

标签: linux shell process process-group

如何设置shell脚本的进程组?此外,我希望所有子进程都在同一个进程组中

我希望在C中与setpgid()类似。

6 个答案:

答案 0 :(得分:18)

作为PSkocik points out,可以通过激活作业控制(“监控模式”)在大多数shell中在其自己的进程组中运行进程。

(set -m; exec process_in_its_own_group)

Linux有一个setsid实用程序,它在自己的session中运行作为参数传递的命令(使用eponymous system call)。这比在自己的process groupàlasetpgrp中运行它更强大,但这可能适合您的目的。

如果要将进程放在现有组而不是自己的组中(即,如果您想要setpgid的全部功能),则没有常见的shell实用程序。你必须使用C / Perl / ...

答案 1 :(得分:10)

我会回答我理解的部分内容:

如何强制当前bash shell脚本成为自我进程组:

我把它放在我的bash脚本的开头:

pgid_from_pid() {
    local pid=$1
    ps -o pgid= "$pid" 2>/dev/null | egrep -o "[0-9]+"
}

pid="$$"
if [ "$pid" != "$(pgid_from_pid $pid)" ]; then
    exec setsid "$(readlink -f "$0")" "$@"
fi

为什么我需要这个?

交互式 bash会话启动程序时,它会获得自己的新进程组。但是,如果从bash脚本(非交互式)调用程序,则情况并非如此。如果您的程序在两种情况下都依赖于进程组所有者,那么您需要这样做。

答案 2 :(得分:9)

我不认为Bourne,bash或zsh会让你这样做,但是你可以使用内置的setpgrp在perl中完成它(请注意与POSIX略有不同的名称)。将零作为PID传递以修改perl进程本身的组:

setpgrp(0, 12345) || die "$!"

您可能认为可以使用来自bash的perl来设置bash进程的组(例如,通过将$$传递给perl脚本),但我不认为perl进程可以使用修改它没有分叉的进程组。

根据您尝试做的事情,各种shell中的作业控制功能可能会以不同的方式为您提供所需的内容,例如,如果您只是想从终端分离。

更新:我觉得奇怪的是,这个答案已经收到了几个下选票而没有明确解释原因。我的猜测是,downvoters误解了这个问题,即询问如何更改 当前 shell的进程组。或许他们知道如何从shell中做一个setpgrp,但是保守秘密。

答案 3 :(得分:4)

如果您打开set -m,新流程将在新流程组中生成,如果他们已经落后,他们将无法忽略SIGINT和SIGQUIT。

if  [ $$ = $(ps -o pgid -hp $$) ]; then
   echo already a process group leader;
else
   set -m
   $0 "$@" #optionally with &
   set +m
fi

新进程程序组在set -m接管后作为终端的前台进程组运行,除非它们在后台运行。

如果实施支持"用户可移植性实用程序",set -m显然是半标准的,POSIX要求它。 在实践中,它适用于bashdashkshpdkshshyashzshposh没有。

答案 4 :(得分:1)

正如@Rob Davis在his answer中指出的那样,设置进程组并不是你想要的shell。

相反,您希望使用其过程控制机制。 This answer涵盖了 linux 承载上的sh。简而言之:

#! /bin/sh
# Kill all opened jobs on exit.
trap 'kill $(jobs -p)' EXIT

这将终止在backrground中打开的所有作业(例如使用&)。

答案 5 :(得分:1)

如果您打算清除任何衍生的子Shell进程(即使脚本本身不是直接从交互式Shell启动,而是从另一个进程直接启动,因此不是),这是从其他几个好的答案中得出的最新结论。 t会自动成为其自己的进程组组长),并在必要时将当前脚本重新启动为新的进程组组长。

# First, obtain the current PGID, by parsing the output of "ps".
pgid=$(($(ps -o pgid= -p "$$")))

# Check if we're already the process group leader; if not, re-launch ourselves.
# Use setsid instead of set -m (...) to avoid having another subshell in between. This helps that the trap gets executed when the script is killed.
[ $$ -eq $pgid ] || exec setsid "${BASH_SOURCE[0]}" "$@"

# Kill any subshell processes when the script exits.
trap "kill -- -$pgid" EXIT
# Note: If the script only starts background jobs, and that's all you care about, you can replace all of the above with this simple trap:
#trap "jobs -p | xargs kill --" EXIT  # Kill remaining jobs when the script exits.