web-dev-qa-db-ja.com

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

シェルスクリプトをもっと読みやすいpythonバージョンに移植しようとしています。オリジナルのシェルスクリプトはバックグラウンドで "&"でいくつかのプロセス(ユーティリティ、モニタなど)を開始します。どのように私はpythonで同じ効果を達成することができますか?私はこれらのプロセスがpythonスクリプトが完了したときに死なないようにしたいと思います。それがデーモンの概念にどういうわけか関連していると確信しています、しかし私はこれを簡単にする方法を見つけることができませんでした。

239
Artem

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

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


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

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

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

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

72
jkp

jkp の解決策はうまくいきますが、新しいやり方(およびドキュメントが推奨するやり方)はsubprocessモジュールを使うことです。単純なコマンドと同等ですが、複雑なことをしたい場合にはより多くのオプションがあります。

あなたのケースの例:

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

これはバックグラウンドでrm -r somefileを実行するはずです。 しかし注意してください:subprocess.Popen()はバックグラウンドでプロセスを実行するだけです。ただし、pythonスクリプト内の何も実行されているコマンドの出力に依存しない場合:

たとえば、次のコマンドはnotをバックグラウンドで実行します。

import subprocess
ls_output=subprocess.Popen(["ls", "-a"], stdout=subprocess.PIPE)

こちらのドキュメント を参照してください

また、明確化のポイント: "バックグラウンド"純粋にシェルの概念:おそらくあなたが望むことは新しいプロセスを生み出すことです。ここでは "バックグラウンド"をシェルのバックグラウンドのような振る舞いを指すために使用しましたが、実際にプロセスがバックグラウンドにあることを間違えないでください。

319
Dan

あなたはおそらく 「Pythonで外部コマンドを呼び出す方法」 への答えが欲しいでしょう。

最も簡単な方法はos.system関数を使うことです。例えば:

import os
os.system("some_command &")

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

37
Eli Courtwright

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

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

解決策は、win APIの基礎となるCreateProcess関数にDETACHED_PROCESSプロセス作成フラグ を渡すことです。もしあなたがたまたまpywin32をインストールしたのなら、win32processモジュールからフラグをインポートすることができます。

DETACHED_PROCESS = 0x00000008

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

close_fds=Trueパラメータを指定してsubprocess.Popen()を使用すると、生成されたサブプロセスを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'
18
Jimmy Yin

おそらく、対話型セッションを開いてhelp(os)を発行して、さまざまなスレッドをフォークするためのosモジュールの調査を開始します。関連する関数はforkとexecのいずれかです。どのように起動するかについてのアイデアを与えるために、フォークを実行する関数にこのようなものを入れてください(関数はプログラム名とそのパラメータを含む引数としてリストかTuple 'args'をとる必要があります。新しいスレッドに対してstdin、out、およびerrを定義します。

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)

出力をキャプチャし、threadingを使用してバックグラウンドで実行します。

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

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

threadingモジュール はそれを可能にします。

最初に、この質問の中で出力リダイレクト部分を単独で行う方法を見てください。 Python Popen:標準出力とログファイルに同時に書き込む

その後:

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

2行ごとにstdoutが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でテスト済み。