web-dev-qa-db-ja.com

シェルスクリプトのプロセスグループを設定する方法

シェルスクリプトのプロセスグループを設定するにはどうすればよいですか?また、すべての子プロセスを同じプロセスグループに入れたい

Cのsetpgid()に似たものを期待しています。

30
Jacob

PSkocikが指摘 のように、ほとんどのシェルでは、ジョブ制御(「監視モード」)をアクティブにすることで、独自のプロセスグループでプロセスを実行できます。

(set -m; exec process_in_its_own_group)

Linuxには setsid ユーティリティがあり、引数として渡されたコマンドを独自に実行します セッション同名のシステムコール を使用) 。これは、それ自体で実行するよりも強力です プロセスグループ àla setpgrp ですが、目的には問題ない場合があります。

プロセスを独自のグループではなく既存のグループに配置する場合(つまり、setpgidの全機能が必要な場合)、一般的なシェルユーティリティはありません。 C/ Perl /…を使用する必要があります

18
Gilles

私が理解していることの一部に答えます:

現在の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スクリプトから呼び出された場合(非対話型)はそうではありません。プログラムが両方の条件でプロセスグループの所有者であることに依存している場合は、これが必要になります。

10
vaab

Bourne、bash、またはzshでそれができるとは思いませんが、組み込みのsetpgrpを使用してPerlで実行できます(POSIXとのわずかな名前の違いに注意してください)。 Perlプロセス自体のグループを変更するには、PIDとしてゼロを渡します。

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

たとえば、bashからPerlを使用してbashプロセスのグループを設定できると思うかもしれませんが(たとえば、$$をPerlスクリプトに渡すことによって)、Perlプロセスが変更できるとは思いません。フォークしなかったプロセスのグループ。

何をしようとしているかに応じて、さまざまなシェルのジョブ制御機能により、ターミナルから切り離したい場合など、さまざまな方法で必要なものが提供される場合があります。

PDATE:この回答が、理由を明確に説明せずに2、3の反対票を獲得したのは奇妙だと思います。私の推測では、反対派は、currentシェルのプロセスグループを変更する方法を尋ねている質問を誤解していると思います。あるいは、シェルからsetpgrpを実行する方法を知っているが、秘密を守っているのかもしれません。

9
Rob Davis

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で必要です。実際には、bashdashkshpdkshshyash、およびzshで機能します。 poshにはありません。

4
PSkocik

生成されたサブシェルプロセスをクリーンアップすることを意図している場合(スクリプト自体がインタラクティブシェルから直接起動されていない場合でも、別のプロセスから起動されているため、自動的に起動されない場合でも)、ここで他のいくつかの良い答えから取られた後期合成があります独自のプロセスグループリーダー)、必要に応じて現在のスクリプトを新しいプロセスグループリーダーとして再起動します。

# 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.
1
Ingo Karkat

@Rob Davisが 彼の答え で指摘したように、プロセスグループの設定はシェルに必要なものではありません。

代わりに、プロセス制御メカニズムを使用する必要があります。 この回答linuxおよびborneでshに対してこれを行う方法について説明します。要するに:

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

これにより、バックグラウンドで開かれたすべてのジョブが強制終了されます(例:&)。

1
NHDaly