開始したばかりのプロセスのPIDを取得する方法


71

プロセス(例:myCommand)を開始し、そのpidを取得します(後で強制終了できるようにします)。

私はpsを試し、名前でフィルタリングしましたが、名前でプロセスを区別できません

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

プロセス名は一意ではないためです。

プロセスを実行するには:

myCommand &

このPIDは次の方法で取得できます。

echo $!

もっと簡単な解決策はありますか?

myCommandを実行して、1行のコマンドの結果としてそのPIDを取得できれば幸いです。

回答:


77

何がより簡単になりecho $!ますか?1行として:

myCommand & echo $!

これらのコマンドを「&」とマージしてくれてありがとう。
-rafalmag

8
bashスクリプト、プログラムを開始するループ、$!正確ではありません。スクリプト自体のpidを返すこともあれば、スクリプトからgrepまたはawkを実行することで、このシナリオで起動したばかりのプロセスのpidを具体的に取得するソリューションがありますか?PID =のようなものはmyprogram素晴らしいされているだろう

2
ただし&、前の行としてa を使用してコマンドを開始する必要があります。そうでない場合、エコーは基本的に空白を返します。
-rogerdpack

2
変数のような変数に割り当てるとcommand & echo $!、このステップで実行がフリーズします:(
Shashank Vivek

26

コマンドを小さなスクリプトでラップする

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file

20

sh -cおよびexecを使用して、コマンドの実行であってもコマンドのPIDを取得できます。

を開始してmyCommand、実行を開始する前にPIDが出力されるようにするには、次を使用できます。

sh -c 'echo $$; exec myCommand'

使い方:

これにより、新しいシェルが開始され、そのシェルのPIDが出力されます。次に、execビルトインを使用してシェルをコマンドに置き換え、同じPIDを持つようにします。シェルがexecビルトインでコマンドを実行すると、シェルは実際にはそのコマンドになります。これは、それ自体の新しいコピーをフォークする一般的な動作ではなく、独自のPIDを持ち、コマンドになります。

これは、非同期実行(with &)、ジョブ制御、またはを使用しpsた検索を含む代替手段よりもはるかに簡単であることがわかりました。これらのアプローチは問題ありませんが、使用する特別な理由がない限り(たとえば、コマンドが既に実行されている場合は、その場合、PIDを検索するか、ジョブ制御を使用するのが理にかなっています)、まずこの方法を検討することをお勧めします。(そして、私は確かにこれを達成するために複雑なスクリプトや他のプログラムを書くことを考えないでしょう)。

この回答には、この手法の例が含まれています。


そのコマンドの一部は時々省略できますが、通常は省略できません。

使用しているシェルがBourneスタイルでありexec、これらのセマンティクスを備えたビルトインをサポートしている場合でも、通常、この目的のためにsh -c新しい(別個の)シェルプロセスを作成するために(または同等の)使用を避けないでください:

  • シェルがになったら、myCommand後続のコマンドの実行を待機しているシェルはありません。sh -c 'echo $$; exec myCommand; foofoo、自分自身をに置き換えた後に実行しようとすることはできませんmyCommand。これを最後のコマンドとして実行するスクリプトを書いていない限り、echo $$; exec myCommand他のコマンドを実行しているシェルで使用することはできません。
  • これにはサブシェルを使用できません。(echo $$; exec myCommand)は構文的にはの方がいいかもしれませんsh -c 'echo $$; exec myCommand'が、$$inside を実行する( )と、サブシェル自体ではなく、親シェルのPIDが得られます。しかし、新しいコマンドのPIDになるのはサブシェルのPIDです。一部のシェルは、サブシェルのPIDを見つけるための独自の非移植メカニズムを提供します。これは、これに使用できます。特に、Bash 4では(echo $BASHPID; exec myCommand)機能します。

最後に、一部のシェルは、シェルがその後何もする必要がないことがわかっている場合、コマンドを実行するように最適化を実行することに注意してくださいexec。実行する最後のコマンドであるときにいつでもこれを実行しようとするシェルもあれば、コマンドの前後に他のコマンドがない場合にのみ実行するシェルもあれば、まったく実行しないシェルもあります。効果は、書くのexecを忘れて使用するだけの場合sh -c 'echo $$; myCommand'いくつかのシェルを備えたいくつかのシステムで正しいPIDを提供することです。そのような振る舞いに依存することはお勧めせず、代わりにいつそれが必要かを常に含めます。exec


を実行する前にmyCommand、bashスクリプトでいくつかの環境変数を設定する必要があります。これらexecは、コマンドが実行されている環境に引き継がれますか?
user5359531

私の環境はexecコマンドに引き継がれているようです。ただし、この方法はmyCommand、他のプロセスを開始するときには機能しません。他のプロセスは、作業する必要があるプロセスです。私が発行したときにkill -INT <pid>どこにpidこのように得られた、サブプロセスに到達していない信号がによって始めmyCommand、私が実行した場合のに対し、myCommand 現在のセッションとCtrl + Cで、信号が正しく伝播します。
-user5359531

1
これを試しましたが、myCommandプロセスのpidはecho $$ +1によるpid出力のようです。私は何か間違っていますか?
クロバー

私のコマンドは次のようになりますsh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
。– crobar

7

より簡単な解決策は知りませんが、$を使用していません!十分ですか?他の人が言ったように、後で必要な場合は、いつでも他の変数に値を割り当てることができます。

サイドノートとして、psからパイプする代わりにpgrepまたはを使用できますpidof


5

pidをファイルに登録した後、bashスクリプトからexecを使用します。

例:

args p1、p2、p3で実行したい「forever.sh」という名前のスクリプトがあるとします

forever.shソースコード:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

reaper.shを作成します。

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

reaper.shからforever.shを実行します。

./reaper.sh ./forever.sh p1 p2 p3 p4 &

forever.shは、5秒ごとにsyslogに行を記録するだけです。

あなたは今/var/run/forever.sh.pidにpidを持っています

cat /var/run/forever.sh.pid 
5780

そしてforever.shはaokを実行しています。syslog grep:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

プロセステーブルで確認できます。

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4

3
ああ、そして「oneliner」:/ bin / sh -c 'echo $$> / tmp / my.pid && exec program args'&
user237419

1
引数内の内部空白を適切に保持するexec "$@"には、の代わりに使用する必要がありますexec $*。技術的に保存する必要があるのは、空白ではなく、IFSシェルパラメーター(デフォルトではスペース、タブ、改行)の文字の出現です。
クリスジョンセン

取られたポイント。:)
user237419

ありがとう、私は$$パラメータを知りませんでした。とても便利です。
-rafalmag

3

bashシェルでは、代わりにビルトインを使用$!できますjobs -p。場合によっては、変数展開の前(または変数展開の代わり)にシェルが!in $!を解釈し、予期しない結果をもたらします。

たとえば、これは機能しません。

((yourcommand) & echo $! >/var/run/pidfile)

これは:

((yourcommand) & jobs -p >/var/run/pidfile)

私はあなたが((yourcommand)&jobs -p> / var / run / pidfile)を意味したと思います。
ドム

1

次のようなものを使用できます。

$ myCommand ; pid=$!

または

$ myCommand && pid=$!

2つのコマンドは、;またはを使用したジョイントにすることができます&&。2番目の場合、pidは最初のコマンドが成功した場合にのみ設定されます。プロセスIDはから取得できます$pid


3
OPはPIDを取得したいので、後で殺すことができます。; および&&は、エコー$!の前に元のプロセスが終了することを要求します。実行されます。
user9517

はい、あなたは正しいです。これにより、myCommandが終了した後にpidが得られます。
ハレド

6
コマンドセパレーターの左側で開始されたプロセスのPIDを参照した$!後、&&または参照;することはありません。$!非同期に起動されたプロセスに対してのみ設定されます(通常&、一部のシェルには他のメソッドもあります)。
クリスジョンセン

それは有用であり、なぜ誰も私に賛成票を投じないのですか?しかし良い仕事:)

1

これはちょっとしたハック的な答えであり、ほとんどの人にはうまくいかないでしょう。また、これはセキュリティリスクが非常に大きいため、安全であり、入力がサニタイズされていると確信している場合を除き、実行しないでください。

ここで小さなCプログラムをstart(または、必要な)と呼ばれるバイナリにコンパイルし、プログラムを次のように実行します。./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

要するに、これはPIDをに出力しstdout、プログラムをプロセスにロードします。それでも同じPIDが必要です。


このコードによって返されたPID番号をbash変数に記録するにはどうすればよいですか?私にstdoutは不明な理由で、この例では記録されていないようですRESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
。– Tfb9

@ Tfb9:正直に他のアプローチのいずれかをお勧めします。これは2年以上前に書いたもので、ハッカーやエラーが発生しやすい方法の1つです(またはそうだと思います)。
tonysdg

注意していただきありがとうございます。私はこれで私の問題を解決したと思うsleep 10 & PID_IS=$!; echo $PID_IS
-Tfb9
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.