Pythonでバックグラウンドプロセスを開始する方法


295

シェルスクリプトをより読みやすいpythonバージョンに移植しようとしています。元のシェルスクリプトは、「&」でバックグラウンドでいくつかのプロセス(ユーティリティ、モニターなど)を開始します。Pythonで同じ効果を得るにはどうすればよいですか?Pythonスクリプトが完了したときに、これらのプロセスが停止しないようにしてください。どういうわけかデーモンの概念に関連していると思いますが、簡単にこれを行う方法を見つけることができませんでした。



1
こんにちはArtem。Danの回答を受け入れてください。(1)より多くの投票、(2)subprocess.Popen()2010年以降の新しい推奨方法(現在は2015年です)、および(3)ここにリダイレクトされる重複した質問にもに関する受け入れられた回答があるためsubprocess.Popen()です。乾杯:-)
olibre 2015年

1
@olibre実際、答えはsubprocess.Popen("<command>")適切なシバンが率いる<command>ファイルであるはずです。私(Debian)にとってbashおよびpythonスクリプトで完璧に動作し、暗黙的にshellsを実行し、その親プロセスを存続させます。stdout親のものと同じターミナルに行きます。したがって、&これはOPの要求であったシェルのように機能します。しかし、地獄、少しのテストがすぐにそれを示した間、すべての質問は非常に複雑になりました;)
フラシュビエ

背景については、stackoverflow.com
a / 51950538/874188

回答:


84

:この回答は2009年に投稿されたときよりも最新ではありません。subprocess他の回答に示されているモジュールを使用することは、ドキュメントで推奨されています

(サブプロセスモジュールは、新しいプロセスを生成して結果を取得するためのより強力な機能を提供することに注意してください。これらの関数を使用するよりもそのモジュールを使用することをお勧めします。)


プロセスをバックグラウンドで開始したい場合system()は、シェルスクリプトと同じように使用して呼び出すか、次のようにできspawnます。

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(または、代わりに、移植性の低いos.P_NOWAITフラグを試すこともできます)。

こちらのドキュメントをご覧ください


8
備考:実行可能ファイルへのフルパスを指定する必要があります。この関数はPATH変数を使用せず、それを使用するバリアントはWindowsでは使用できません。
ソリン

36
私のためにまっすぐにPythonをクラッシュさせます。
パブロ

29
os.P_DETACHはos.P_NOWAITに置き換えられました。
自分

7
を使用するsubprocessことを提案している人々は、私たちにプロセスを切り離す方法のヒントを教えてくれsubprocessますか?
rakslice 2014

3
attach.py​​がバックグラウンドでsome_long_running_progを読み書きできるように、Pythonスクリプト(attach.py​​など)を使用してバックグラウンドプロセスを見つけ、そのIOをリダイレクトするにはどうすればよいですか?
raof01 14

376

一方でJKPのソリューションの作品、物事の新しい方法(およびドキュメントが推奨する方法)が使用することですsubprocessモジュールを。単純なコマンドの場合は同等ですが、複雑なことをしたい場合はより多くのオプションを提供します。

あなたのケースの例:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

これはrm -r some.fileバックグラウンドで実行されます。.communicate()から返されたオブジェクトを呼び出すと、Popen完了するまでブロックされるので、バックグラウンドで実行する場合は実行しないでください。

import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate()  # Will block for 30 seconds

こちらのドキュメントをご覧ください

また、明確化のポイント:ここで使用する「背景」は純粋にシェルの概念です。技術的には、プロセスが完了するのを待つ間、プロセスをブロックせずに生成したいということです。ただし、ここでは「バックグラウンド」を使用して、シェルのバックグラウンドに似た動作を示しています。


7
@ダン:バックグラウンドで実行中のプロセスを強制終了するにはどうすればよいですか?しばらく実行して(対話するデーモンです)、使い終わったら終了させたいです。ドキュメントは役に立ちません...
Juan

1
@ダンが、そのためのPIDを知る必要はありませんか?アクティビティモニター/タスクマネージャーはオプションではありません(プログラムで実行する必要があります)。
フアン

5
それでは、Popen()の結果をstdinに書き込む必要があるときに、プロセスをどのようにしてバックグラウンドに強制しますか?
マイケル

2
@JFSebastian:「現在のプログラムの実行を停止しない独立したプロセスを作成するにはどうすればよいですか」と解釈しました。これをより明確にするためにどのように編集することをお勧めしますか?
Dan

1
@ダン:正解は次のとおりです。Popen()メインスレッドのブロックを回避するために使用し、デーモンが必要な場合は、python-daemonパッケージを調べて、明確に定義されたデーモンの動作を理解してください。サブプロセスのドキュメントへのリンクを除いて、「しかし注意してください」で始まるすべてを削除すれば、あなたの答えは問題ありません。
jfs 2014

47

「Pythonで外部コマンドを呼び出す方法」への回答が必要になるでしょう。

最も簡単な方法は、os.system関数を使用することです。例:

import os
os.system("some_command &")

基本的に、system関数に渡したものはすべて、スクリプトでシェルに渡した場合と同じように実行されます。


9
私見では、Pythonスクリプトは通常、クロスプラットフォームになるように記述されており、単純なクロスプラットフォームソリューションが存在する場合は、それに固執することをお勧めします。将来、別のプラットフォームで作業する必要があるかどうかは決してわかりません:)または、他の人がスクリプトを自分のプラットフォームに移行したいと思うかどうか。
d9k 2016年

7
このコマンドは同期です(つまり、開始されたプロセスの終了を常に待ちます)。
tav

@ d9kはos.systemは移植できませんか?
lucid_dreamer

1
@ d9kは、バックグラウンドで何かを実行するという選択が、すでにposix-landにいるのではないですか?Windowsで何をしますか?サービスとして実行しますか?
lucid_dreamer

特定のフォルダーからコマンドを実行する必要がある場合、これをどのように使用できますか?
mrRobot

29

私はこれをここで見つけまし

Windows(win xp)では、親プロセスは、longtask.pyが作業を完了するまで完了しません。それはあなたがCGIスクリプトで望むものではありません。この問題はPythonに固有のものではなく、PHPコミュニティでも問題は同じです。

解決策は、DETACHED_PROCESS プロセス作成フラグCreateProcesswin APIの基になる関数に渡すことです。pywin32をインストールした場合は、win32processモジュールからフラグをインポートできます。それ以外の場合は、自分で定義する必要があります。

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

6
プロセスIDを保持する方法を示すための+1。そして、誰もがプロセスIDで、後にプログラムを強制終了する場合:stackoverflow.com/questions/17856928/...
iChux

1
これはWindowsのみのようです
Audrius Meskauskas

24

パラメータsubprocess.Popen()とともに使用すると、close_fds=True生成されたサブプロセスをPythonプロセス自体から切り離し、Pythonが終了した後も実行を継続できます。

https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'

1
Windowsではデタッチしませんが、creationflagsパラメータを使用すると機能します
SmartManoj 2018

3
このソリューションでは、サブプロセスをLinuxのゾンビとして残します。
TitanFighter

:これはセットSIGCHLD SIG_IGNによって回避することができ@TitanFighter stackoverflow.com/questions/16807603/...
sailfish009

ありがとう@Jimmyあなたの答えは私のために働く唯一の解決策です。
sailfish009

12

おそらく、さまざまなスレッドをforkするためにosモジュールの調査を開始する必要があります(対話式セッションを開き、help(os)を発行することにより)。関連する関数はforkとexecの関数です。開始方法についてのアイデアを提供するには、フォークを実行する関数に次のようなものを入れます(関数はリストまたはタプル「args」をプログラムの名前とそのパラメーターを含む引数として取る必要があります。また、新しいスレッドの標準入力、出力、エラーを定義するには):

try:
    pid = os.fork()
except OSError, e:
    ## some debug output
    sys.exit(1)
if pid == 0:
    ## eventually use os.putenv(..) to set environment variables
    ## os.execv strips of args[0] for the arguments
    os.execv(args[0], args)

2
os.fork()は本当に便利ですが、* nixでしか利用できないという欠点があります。
Evan Fosmark 2009

os.forkの唯一の問題は、それがwin32固有であることです。
jkp 2009

このアプローチの詳細:Pythonによるデーモンの作成
Amir Ali Akbari 14

threading次のコマンドでも同様の効果が得られます:stackoverflow.com/a/53751896/895245私はそれがWindowsで動作するかもしれないと思います。
Ciro Santilli郝海东冠状病六四事件法轮功

9

出力のキャプチャとバックグラウンドでの実行の両方 threading

この回答で述べたよう、で出力をキャプチャしstdout=てからしようとするread()と、プロセスがブロックされます。

ただし、これが必要な場合があります。たとえば、それらの間のポートを介して通信する2つのプロセスを起動、それらの標準出力をログファイルと標準出力に保存したいと考えました。

threadingモジュールは、私たちはそれを行うことができます。

まず、この質問で出力リダイレクトの部分だけを行う方法を見てください:Python Popen:stdoutとログファイルに同時に書き込み

次に:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sleep.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

実行後:

./main.py

stdoutは、2行ごとに0.5秒ごとに更新されます。

0
10
1
11
2
12
3
13

各ログファイルには、特定のプロセスの各ログが含まれています。

触発:https : //eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

Ubuntu 18.04、Python 3.6.7でテスト済み。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.