Làm thế nào để bắt đầu một quá trình nền trong Python?


295

Tôi đang cố gắng chuyển một tập lệnh shell sang phiên bản python dễ đọc hơn nhiều. Tập lệnh shell ban đầu bắt đầu một số quy trình (tiện ích, màn hình, v.v.) trong nền với "&". Làm thế nào tôi có thể đạt được hiệu quả tương tự trong trăn? Tôi muốn các quá trình này không chết khi các kịch bản python hoàn thành. Tôi chắc chắn rằng nó liên quan đến khái niệm daemon bằng cách nào đó, nhưng tôi không thể tìm thấy cách làm điều này một cách dễ dàng.


1
Câu hỏi thực sự trùng lặp là Làm thế nào để khởi chạy và chạy tập lệnh bên ngoài trong nền? . Chúc mừng;)
olibre

1
Xin chào. Vui lòng chấp nhận câu trả lời của Dan vì (1) nhiều phiếu hơn, (2) subprocess.Popen()là cách được đề xuất mới kể từ năm 2010 (hiện tại chúng tôi đang ở 2015) và (3) câu hỏi trùng lặp chuyển hướng ở đây cũng là một câu trả lời được chấp nhận subprocess.Popen(). Chúc mừng :-)
olibre

1
@olibre Trong thực tế, câu trả lời phải có subprocess.Popen("<command>")với tệp <lệnh> được dẫn bởi một shebang phù hợp. Hoạt động hoàn hảo đối với tôi (Debian) với các tập lệnh bash và python, hoàn toàn shells và tồn tại quá trình cha mẹ của nó. stdoutđi đến cùng một thiết bị đầu cuối hơn của cha mẹ. Vì vậy, điều này hoạt động giống như &trong một vỏ được yêu cầu OP. Nhưng chết tiệt, tất cả các câu hỏi diễn ra rất phức tạp trong khi một thử nghiệm nhỏ cho thấy nó ngay lập tức;)
flaschbier

Đối với nền có thể xem thêm stackoverflow.com/a/51950538/874188
tripleee

Câu trả lời:


84

Lưu ý : Câu trả lời này ít hiện tại hơn so với khi được đăng vào năm 2009. Sử dụng subprocessmô-đun hiển thị trong các câu trả lời khác hiện được khuyến nghị trong tài liệu

(Lưu ý rằng mô-đun quy trình con cung cấp các phương tiện mạnh hơn để sinh ra các quy trình mới và truy xuất kết quả của chúng; sử dụng mô-đun đó tốt hơn là sử dụng các chức năng này.)


Nếu bạn muốn quá trình của bạn bắt đầu trong nền, bạn có thể sử dụng system()và gọi nó theo cùng một cách mà kịch bản shell của bạn đã làm, hoặc bạn có thể spawn:

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

(hoặc, thay vào đó, bạn có thể thử os.P_NOWAITcờ ít di động hơn ).

Xem tài liệu ở đây .


8
Ghi chú: bạn phải chỉ định đường dẫn đầy đủ để thực thi. Hàm này sẽ không sử dụng biến PATH và biến thể sử dụng nó không có sẵn trong Windows.
sorin

36
thẳng lên đâm trăn cho tôi.
pablo

29
os.P_DETACH đã được thay thế bằng os.P_NOWAIT.
chính mình

7
Những người đề xuất sử dụng có thể subprocesscho chúng tôi một gợi ý làm thế nào để tách một quá trình với subprocess?
rakslice

3
Làm cách nào tôi có thể sử dụng tập lệnh Python (giả sử tệp đính kèm) để tìm quy trình nền và chuyển hướng IO của nó để tệp đính kèm có thể đọc từ / ghi vào some_long_rasty_prog trong nền?
raof01

376

Trong khi giải pháp của jkp hoạt động, cách thức mới hơn (và cách tài liệu khuyến nghị) là sử dụng subprocessmô-đun. Đối với các lệnh đơn giản tương đương, nhưng nó cung cấp nhiều tùy chọn hơn nếu bạn muốn làm điều gì đó phức tạp.

Ví dụ cho trường hợp của bạn:

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

Điều này sẽ chạy rm -r some.filetrong nền. Lưu ý rằng việc gọi .communicate()đối tượng được trả về Popensẽ chặn cho đến khi hoàn thành, vì vậy đừng làm điều đó nếu bạn muốn nó chạy trong nền:

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

Xem tài liệu ở đây .

Ngoài ra, một điểm cần làm rõ: "Bối cảnh" khi bạn sử dụng nó ở đây hoàn toàn là một khái niệm vỏ; về mặt kỹ thuật, điều bạn muốn nói là bạn muốn sinh ra một quy trình mà không chặn trong khi bạn chờ nó hoàn thành. Tuy nhiên, tôi đã sử dụng "nền" ở đây để chỉ hành vi giống như nền.


7
@Dan: Làm thế nào để tôi giết tiến trình một khi nó chạy trong nền? Tôi muốn chạy nó một lúc (đó là một daemon mà tôi tương tác) và giết nó khi tôi hoàn thành nó. Các tài liệu không hữu ích ...
Juan

1
@Dan nhưng tôi không cần biết PID cho điều đó? Giám sát hoạt động / Trình quản lý tác vụ không phải là một tùy chọn (cần phải xảy ra theo chương trình).
Juan

5
ok vậy làm thế nào để bạn buộc quá trình làm nền khi bạn cần kết quả của Popen () để viết vào stdin của nó?
Michael

2
@JFSebastian: Tôi đã giải thích nó là "làm thế nào tôi có thể tạo ra một quy trình độc lập mà không dừng việc thực thi chương trình hiện tại". Làm thế nào bạn có thể đề nghị tôi chỉnh sửa nó để làm cho điều này rõ ràng hơn?
Dan

1
@Dan: câu trả lời đúng là: sử dụng Popen()để tránh chặn luồng chính và nếu bạn cần một daemon thì hãy nhìn vào python-daemongói để hiểu cách một daemon được xác định rõ sẽ hoạt động như thế nào. Câu trả lời của bạn là ổn nếu bạn xóa mọi thứ bắt đầu bằng "Nhưng hãy cảnh giác" ngoại trừ liên kết đến tài liệu của quy trình con.
jfs

47

Bạn có thể muốn câu trả lời cho "Cách gọi một lệnh bên ngoài trong Python" .

Cách tiếp cận đơn giản nhất là sử dụng os.systemhàm, ví dụ:

import os
os.system("some_command &")

Về cơ bản, bất cứ điều gì bạn chuyển đến systemhàm sẽ được thực thi giống như khi bạn chuyển nó vào trình bao trong tập lệnh.


9
IMHO, các kịch bản python thường được viết là đa nền tảng và nếu có giải pháp đa nền tảng đơn giản thì tốt hơn là gắn bó với nó. Không bao giờ biết nếu bạn sẽ phải làm việc với nền tảng khác trong tương lai :) Hoặc nếu một người đàn ông khác muốn chuyển tập lệnh của bạn sang nền tảng của anh ấy.
d9k

7
Lệnh này là đồng bộ (tức là nó luôn chờ kết thúc quá trình bắt đầu).
tav

@ d9k là os.system không xách tay?
lucid_dreamer

1
@ d9k không phải là sự lựa chọn để chạy một cái gì đó trong nền đã định vị bạn trong vùng đất tích hợp? Bạn sẽ làm gì trên Windows? Chạy như một dịch vụ?
lucid_dreamer

Làm thế nào tôi có thể sử dụng điều này nếu tôi cần chạy một lệnh từ một thư mục cụ thể?
mrRobot

29

Tôi tìm thấy cái này ở đây :

Trên windows (win xp), tiến trình cha sẽ không kết thúc cho đến khi longtask.pyhoàn thành công việc. Nó không phải là những gì bạn muốn trong CGI-script. Vấn đề không cụ thể đối với Python, trong cộng đồng PHP các vấn đề là như nhau.

Giải pháp là chuyển DETACHED_PROCESS Cờ tạo quy trình cho CreateProcesshàm bên dưới trong win win. Nếu bạn đã cài đặt pywin32, bạn có thể nhập cờ từ mô-đun win32 Process, nếu không, bạn nên tự xác định nó:

DETACHED_PROCESS = 0x00000008

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

6
+1 để hiển thị cách giữ lại id quá trình. Và nếu bất cứ ai muốn giết chương trình sau này với id quá trình: stackoverflow.com/questions/17856928/
mẹo

1
Điều này dường như chỉ có trên Windows
Audrius Meskauskas

24

Sử dụng subprocess.Popen()với close_fds=Truetham số, điều này sẽ cho phép các tiến trình con được sinh ra tách rời khỏi chính tiến trình Python và tiếp tục chạy ngay cả khi Python thoát ra.

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
Trong các cửa sổ, nó không tách ra nhưng sử dụng tham số
Creationflags

3
Giải pháp này để lại một quy trình con như Zombie trên Linux.
TitanFighter

@TitanFighter có thể tránh điều này bằng cách đặt SIGCHLD SIG_IGN: stackoverflow.com/questions/16807603/
sailfish009

cảm ơn @Jimmy câu trả lời của bạn là giải pháp DUY NHẤT làm việc cho tôi.
cá cờ009

12

Bạn có thể muốn bắt đầu điều tra mô-đun os để tìm các luồng khác nhau (bằng cách mở một phiên tương tác và phát hành trợ giúp (os)). Các chức năng có liên quan là ngã ba và bất kỳ của những người thực hiện. Để cung cấp cho bạn ý tưởng về cách bắt đầu, hãy đặt một cái gì đó như thế này vào một hàm thực hiện rẽ nhánh (hàm cần lấy một danh sách hoặc tuple 'args' làm đối số chứa tên của chương trình và các tham số của nó; bạn cũng có thể muốn để xác định stdin, out và err cho chủ đề mới):

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()thực sự hữu ích, nhưng nó có một nhược điểm đáng chú ý là chỉ có sẵn trên * nix.
Evan Fosmark

Vấn đề duy nhất với os.fork là nó là win32 cụ thể.
jkp

Thêm chi tiết về cách tiếp cận này: Tạo một daemon theo cách của Python
Amir Ali Akbari

Bạn cũng có thể đạt được các hiệu ứng tương tự với threading: stackoverflow.com/a/53751896/895245 Tôi nghĩ rằng nó có thể hoạt động trên Windows.
Ciro Santilli 郝海东 冠状 病 事件

9

Cả hai bắt đầu ra và chạy trên nền với threading

Như đã đề cập trong câu trả lời này , nếu bạn nắm bắt được đầu ra stdout=và sau đó thử read(), thì các khối quy trình.

Tuy nhiên, có những trường hợp bạn cần điều này. Ví dụ, tôi muốn khởi chạy hai tiến trình nói chuyện qua một cổng giữa chúng và lưu thiết bị xuất chuẩn của chúng vào tệp nhật ký và thiết bị xuất chuẩn.

Các threadingmô-đun cho phép chúng tôi làm điều đó.

Trước tiên, hãy xem cách thực hiện phần chuyển hướng đầu ra một mình trong câu hỏi này: Python Popen: Viết vào thiết bị xuất chuẩn VÀ ghi nhật ký đồng thời

Sau đó:

chính

#!/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()

ngủ ngon

#!/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)

Sau khi chạy:

./main.py

thiết bị xuất chuẩn được cập nhật cứ sau 0,5 giây cho mỗi hai dòng chứa:

0
10
1
11
2
12
3
13

và mỗi tệp nhật ký chứa nhật ký tương ứng cho một quy trình nhất định.

Lấy cảm hứng từ: https://eli.thegreenplace.net/2017/interacting-with-a-long-rucky-child- Process-in-python /

Đã thử nghiệm trên Ubuntu 18.04, Python 3.6.7.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.