如何设置shell脚本的进程组?此外,我希望所有子进程都在同一个进程组中
我希望在C中与setpgid()类似。
答案 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要求它。
在实践中,它适用于bash
,dash
,ksh
,pdksh
,sh
,yash
和zsh
。 posh
没有。
答案 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.