シェルスクリプトのプロセスグループを設定するにはどうすればよいですか?また、すべての子プロセスを同じプロセスグループに入れたい
Cのsetpgid()に似たものを期待しています。
PSkocikが指摘 のように、ほとんどのシェルでは、ジョブ制御(「監視モード」)をアクティブにすることで、独自のプロセスグループでプロセスを実行できます。
(set -m; exec process_in_its_own_group)
Linuxには setsid
ユーティリティがあり、引数として渡されたコマンドを独自に実行します セッション ( 同名のシステムコール を使用) 。これは、それ自体で実行するよりも強力です プロセスグループ àla setpgrp
ですが、目的には問題ない場合があります。
プロセスを独自のグループではなく既存のグループに配置する場合(つまり、setpgid
の全機能が必要な場合)、一般的なシェルユーティリティはありません。 C/ Perl /…を使用する必要があります
私が理解していることの一部に答えます:
現在のbashシェルスクリプトを強制的にセルフプロセスグループにする方法:
私はこれを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スクリプトから呼び出された場合(非対話型)はそうではありません。プログラムが両方の条件でプロセスグループの所有者であることに依存している場合は、これが必要になります。
Bourne、bash、またはzshでそれができるとは思いませんが、組み込みのsetpgrp
を使用してPerlで実行できます(POSIXとのわずかな名前の違いに注意してください)。 Perlプロセス自体のグループを変更するには、PIDとしてゼロを渡します。
setpgrp(0, 12345) || die "$!"
たとえば、bashからPerlを使用してbashプロセスのグループを設定できると思うかもしれませんが(たとえば、$$
をPerlスクリプトに渡すことによって)、Perlプロセスが変更できるとは思いません。フォークしなかったプロセスのグループ。
何をしようとしているかに応じて、さまざまなシェルのジョブ制御機能により、ターミナルから切り離したい場合など、さまざまな方法で必要なものが提供される場合があります。
PDATE:この回答が、理由を明確に説明せずに2、3の反対票を獲得したのは奇妙だと思います。私の推測では、反対派は、currentシェルのプロセスグループを変更する方法を尋ねている質問を誤解していると思います。あるいは、シェルからsetpgrpを実行する方法を知っているが、秘密を守っているのかもしれません。
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
にはありません。
生成されたサブシェルプロセスをクリーンアップすることを意図している場合(スクリプト自体がインタラクティブシェルから直接起動されていない場合でも、別のプロセスから起動されているため、自動的に起動されない場合でも)、ここで他のいくつかの良い答えから取られた後期合成があります独自のプロセスグループリーダー)、必要に応じて現在のスクリプトを新しいプロセスグループリーダーとして再起動します。
# 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.