كيف أقوم بإغلاق أنبوب stdout عند قتل عملية بدأت باستخدام عملية python الفرعية Popen؟
-
06-07-2019 - |
سؤال
أتساءل عما إذا كان من الممكن إيقاف تشغيل أنبوب الاتصال عند بدء عملية فرعية في سلسلة رسائل مختلفة.إذا لم أتصل بـ communication()، فستعمل kill() كما هو متوقع، وتنهي العملية بعد ثانية واحدة بدلاً من خمس.
لقد وجدت مناقشة لمشكلة مماثلة هنا, ، لكن لم أحصل على إجابات حقيقية.أفترض أنه يجب علي إما أن أكون قادرًا على إغلاق الأنبوب أو إيقاف العملية الفرعية بشكل صريح (وهي "النوم" في المثال) وقتل ذلك لإلغاء قفل الأنبوب.
لقد حاولت أيضًا العثور على الإجابة عليها على SO، لكنني وجدت فقط هذا و هذا و هذا, ، والتي لا تعالج هذه المشكلة بشكل مباشر بقدر ما أستطيع أن أقول (؟).
لذا فإن الشيء الذي أريد القيام به هو أن أكون قادرًا على تشغيل أمر في سلسلة رسائل ثانية والحصول على كل مخرجاته، ولكن أكون قادرًا على إيقافه على الفور عندما أرغب في ذلك.يمكنني المرور عبر ملف وتتبعه أو ما شابه، ولكن أعتقد أنه يجب أن تكون هناك طريقة أفضل للقيام بذلك؟
import subprocess, time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
process.kill()
هذا هو البرنامج النصي:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "slept five"
انتاج |
$ time python poc.py
process: sleeping five
real 0m5.053s
user 0m0.044s
sys 0m0.000s
المحلول
أعتقد أن المشكلة تكمن في أن Process.kill() يقتل فقط العملية الفرعية المباشرة (bash)، وليس العمليات الفرعية للبرنامج النصي bash.
المشكلة والحل موضح هنا:
- http://www.doughellmann.com/PyMOTW/subprocess/#process-groups-sessions
- كيفية إنهاء عملية بايثون الفرعية التي تم إطلاقها باستخدام shell=True
استخدم Popen(..., preexec_fn=os.setsid) لإنشاء مجموعة عمليات وos.pgkill لقتل مجموعة العمليات بأكملها.على سبيل المثال
import os
import signal
import subprocess
import time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(
args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
os.killpg(process.pid, signal.SIGKILL)
$ time python poc.py
process: sleeping five
real 0m1.051s
user 0m0.032s
sys 0m0.020s
نصائح أخرى
ويبدو لي أن أسهل طريقة للقيام بذلك وتتجنب المشاكل خاصية تعدد سيكون لتعيين إشارة قتل من موضوع الرئيسي، وتحقق لها في موضوع تشغيل البرنامج النصي قبل البلاغ، مما أسفر عن مقتل النصي عندما العلم هو True
.
ويبدو أنك قد تكون ضحية للسوبر الخشنة التزامن بايثون الحبيبات. تغيير السيناريو الخاص بك إلى هذا:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"
وبعد ذلك يصبح الناتج:
process: sleeping five
real 0m5.134s
user 0m0.000s
sys 0m0.010s
إذا ركض النصي بأكمله، سوف يكون الوقت 10S. لذلك يبدو وكأنه لا يعمل موضوع السيطرة الثعبان في الواقع حتى بعد ينام النصي باش. وبالمثل، إذا قمت بتغيير السيناريو الخاص بك إلى هذا:
#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"
وبعد ذلك يصبح الناتج:
process: sleeping five
real 0m1.150s
user 0m0.010s
sys 0m0.020s
وباختصار، التعليمات البرمجية يعمل تنفذ كما منطقيا. :)