এক্সিকিউটিভ সংক্ষিপ্তসার (বা "টিএল; ডাঃ" সংস্করণ): সর্বাধিক একটি রয়েছে যখন এটি সহজ subprocess.PIPE, অন্যথায় এটি শক্ত।
subprocess.Popenএটির জিনিসটি কীভাবে হয় সে সম্পর্কে কিছুটা ব্যাখ্যা করার সময় হতে পারে ।
(ক্যাভিয়েট: এটি পাইথন ২.x এর জন্য, যদিও ৩.x অনুরূপ; এবং আমি উইন্ডোজ ভেরিয়েন্টে বেশ ফাসি। আমি পসিক্স স্টাফটি আরও ভাল বুঝি))
Popenফাংশন শূন্য-টু-তিন ইনপুট / আউটপুট স্ট্রিম, কিছুটা একযোগে সাথে মোকাবিলা করতে হবে। এই প্রকাশ করা হয় stdin, stdoutএবং stderrযথারীতি।
আপনি প্রদান করতে পারেন:
Noneএটি নির্দেশ করে যে আপনি স্ট্রিমটি পুনর্নির্দেশ করতে চান না। পরিবর্তে এটি যথারীতি এগুলির উত্তরাধিকারী হবে। নোট করুন যে POSIX সিস্টেমে কমপক্ষে, এর অর্থ এই নয় যে এটি পাইথনের ব্যবহার করবে sys.stdout, কেবল পাইথনের আসল স্টাডআউট; শেষে ডেমো দেখুন।
- একটি
intমান। এটি একটি "কাঁচা" ফাইল বর্ণনাকারী (কমপক্ষে POSIX এ)। (পার্শ্ব নোট: PIPEএবং STDOUTপ্রকৃতপক্ষে intঅভ্যন্তরীণভাবে তবে এটি "অসম্ভব" বর্ণনাকারী, -1 এবং -2।)
- একটি স্ট্রিম — সত্যই, কোনও
filenoপদ্ধতি সহ কোনও বস্তু । Popenএই স্ট্রিমটির জন্য বর্ণনাকারীটি ব্যবহার করে খুঁজে পাবেন stream.fileno()এবং তারপরে একটি intমান হিসাবে অগ্রসর হবেন ।
subprocess.PIPE, পাইথনের একটি পাইপ তৈরি করা উচিত তা নির্দেশ করে।
subprocess.STDOUT( stderrকেবলমাত্র): পাইথনকে একই বর্ণনাকারী হিসাবে ব্যবহার করতে বলুন stdout। এটি কেবল তখনই অর্থবোধ করে যখন আপনি কোনও (অ- None) মান প্রদান করেছেন stdoutএবং তারপরেও, এটি সেট করা থাকলেই এটি প্রয়োজনstdout=subprocess.PIPE । (অন্যথায় আপনি stdoutযেমন সরবরাহ করেছেন ঠিক একই যুক্তি সরবরাহ করতে পারেন , যেমন, Popen(..., stdout=stream, stderr=stream)))
সবচেয়ে সহজ কেস (কোন পাইপ নেই)
যদি আপনি কিছুই পুনর্নির্দেশ করেন (তিনটিই ডিফল্ট Noneমান হিসাবে বা স্পষ্টত সরবরাহ হিসাবে ছেড়ে যান None), Pipeএটি যথেষ্ট সহজ। এটি কেবল সাবপ্রসেসটি বন্ধ করে চালানো দরকার। বা, যদি আপনি কোনও PIPEঅন- intor ান বা স্ট্রিমের to fileno()এটি এখনও সহজ হিসাবে পুনর্নির্দেশ করেন তবে ওএস সমস্ত কাজ করে। পাইথনকে কেবল উপ-প্রসেসটি বন্ধ করে দেওয়া উচিত, এটির স্টিডিন, স্টডআউট এবং / অথবা স্টার্ডার সরবরাহ করা ফাইল বর্ণনাকারীর সাথে সংযুক্ত করে।
এখনও সহজ কেস: একটি পাইপ
আপনি যদি কেবল একটি স্ট্রিম পুনর্নির্দেশ করেন তবে Pipeএখনও জিনিসগুলি বেশ সহজ। আসুন একবারে একটি স্ট্রিম বাছাই করে দেখুন।
মনে করুন আপনি কিছু সরবরাহ করতে চান stdinতবে পুনর্নির্দেশ করুন stdoutএবং stderrযান বা কোনও ফাইল বিবরণীতে যান। অভিভাবক প্রক্রিয়া হিসাবে, পাইথন write()ডেটা প্রেরণের জন্য আপনার পাইথন প্রোগ্রামটি সহজেই ব্যবহার করা দরকার । আপনি নিজে এটি করতে পারেন, যেমন:
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc.stdin.write('here, have some data\n') # etc
অথবা আপনি স্টিডিন ডেটাতে পাস করতে পারেন proc.communicate()যা stdin.writeউপরের দেখায়। কোনও আউটপুট ফিরে আসেনি তাই communicate()কেবল অন্য একটি আসল কাজ রয়েছে: এটি আপনার জন্য পাইপটি বন্ধ করে দেয়। (আপনি যদি কল না করেন তবে proc.communicate()আপনাকে অবশ্যই proc.stdin.close()পাইপটি বন্ধ করার জন্য কল করতে হবে , যাতে সাব-প্রসেসটি জানতে পারে যে এর মাধ্যমে আর কোনও ডেটা আসছে না))
মনে করুন আপনি ক্যাপচার করতে চান stdoutতবে ছেড়ে চলে যান stdinএবং stderrএকা। আবার এটি সহজ: proc.stdout.read()যতক্ষণ না কোনও আউটপুট না আসে কেবল কল (বা সমমান)) যেহেতু proc.stdout()একটি সাধারণ পাইথন আই / ও স্ট্রিম তাই আপনি এটিতে সমস্ত সাধারণ নির্মাণ ব্যবহার করতে পারেন:
for line in proc.stdout:
বা, আবার, আপনি ব্যবহার করতে পারেন proc.communicate(), যা কেবল আপনার read()জন্য করে।
আপনি যদি কেবল ক্যাপচার করতে চান stderrতবে এটি একই সাথে কাজ করে stdout।
জিনিসগুলি শক্ত হওয়ার আগে আরও একটি কৌশল আছে। মনে করুন আপনি ক্যাপচার করতে চান stdout, এবং ক্যাপচার করতে চান stderrতবে স্ট্যান্ডআউটের মতো একই পাইপে:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
এই ক্ষেত্রে, subprocess"চিটস"! ঠিক আছে, এটি এটি করতে হবে, সুতরাং এটি আসলে প্রতারণা নয়: এটি তার স্টাডাউট এবং তার স্টেডার উভয়কেই (একক) পাইপ-বিবরণীতে নির্দেশিত সাবপ্রসেস শুরু করে যা তার প্যারেন্ট (পাইথন) প্রক্রিয়াতে ফিরে আসে। প্যারেন্ট সাইডে, আউটপুট পড়ার জন্য কেবল আবার একক পাইপ-বর্ণনাকারী রয়েছে। সমস্ত "স্ট্যাডার" আউটপুট প্রদর্শিত হবে proc.stdoutএবং আপনি যদি কল করেন proc.communicate()তবে স্ট্যাডার ফলাফল (টিউপলের দ্বিতীয় মান) হবে Noneস্ট্রিং নয়।
কঠিন ক্ষেত্রে: দুই বা ততোধিক পাইপ
আপনি যখন কমপক্ষে দুটি পাইপ ব্যবহার করতে চান তখন সমস্ত সমস্যা দেখা দেয়। আসলে, subprocessকোড নিজেই এই বিট আছে:
def communicate(self, input=None):
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select() or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
তবে হায়, এখানে আমরা কমপক্ষে দু'টি তৈরি করেছি এবং তিনটি ভিন্ন ভিন্ন পাইপ রয়েছে, সুতরাং count(None)1 বা 0 এর রিটার্ন দেয় আমাদের অবশ্যই শক্ত জিনিসগুলি করতে হবে।
Windows এ, এই ব্যবহারের threading.Threadজন্য জমা ফলাফলে self.stdoutএবং self.stderr, এবং পিতা বা মাতা থ্রেড উদ্ধার হয়েছে self.stdinইনপুট ডেটা (এবং তারপর বন্ধ নল)।
পসিক্সে, এটি pollযদি পাওয়া যায় তবে অন্যথায় selectআউটপুট সংগ্রহ করতে এবং স্টিডিন ইনপুট সরবরাহ করতে ব্যবহার করে। এগুলি সমস্ত (একক) প্যারেন্ট প্রসেস / থ্রেডে চলে।
অচলাবস্থা এড়াতে এখানে থ্রেড বা পোল / সিলেক্ট করা দরকার। ধরুন, উদাহরণস্বরূপ, আমরা তিনটি পৃথক পাইপগুলিতে তিনটি স্ট্রিমকে পুনঃনির্দেশিত করেছি। আরও ধরুন যে লেখার প্রক্রিয়াটি স্থগিত হওয়ার আগে পাইপটিতে কতটা তথ্য স্টাফ করা যেতে পারে তার একটি ছোট সীমা রয়েছে, অন্য প্রান্ত থেকে পাইপটি "পরিষ্কার" করার জন্য অপেক্ষা করছেন। আসুন ছোট ছোট সীমাটি একটি মাত্র বাইটে সেট করুন, কেবল উদাহরণের জন্য। (এটি আসলে জিনিসগুলি কীভাবে কাজ করে তা সীমাবদ্ধতা এক বাইটের চেয়ে অনেক বড় except
পিতা বা মাতা (পাইথন) প্রক্রিয়া চেষ্টা বিভিন্ন বাইট-Say, লিখতে পারেন 'go\n'করতে proc.stdin, প্রথম বাইট যায় এবং তারপর দ্বিতীয় পাইথন প্রক্রিয়া ঘটায় স্থগিত করার, subprocess জন্য অপেক্ষা প্রথম বাইট পড়তে, নল খালি।
এদিকে, ধরুন উপ-প্রসেসটি একটি বন্ধুত্বপূর্ণ "হ্যালো! আতঙ্কিত হবেন না!" প্রিন্ট করার সিদ্ধান্ত নিয়েছে অভিবাদন। Hতার stdout- এ পাইপ মধ্যে যায়, কিন্তু eস্থগিত করার এটা কারণ, তার পিতা বা মাতা যে পড়তে অপেক্ষা H, stdout- এ পাইপ খালি।
এখন আমরা আটকে আছি: পাইথন প্রক্রিয়াটি ঘুমিয়ে আছে, "যাও" বলে শেষ করার জন্য অপেক্ষা করছে, এবং উপশহরটিও ঘুমিয়ে আছে, "হ্যালো! আতঙ্কিত হবেন না!" বলে শেষ করার জন্য অপেক্ষা করছেন।
subprocess.Popenকোড থ্রেডিং-অর-নির্বাচন / ভোট সঙ্গে এই সমস্যা এড়াতে। বাইটগুলি পাইপগুলির উপর দিয়ে যেতে পারলে তারা যায়। যখন তারা পারবেন না, কেবল একটি থ্রেড (পুরো প্রক্রিয়া নয়) ঘুমাতে হবে select বা নির্বাচন / পোলের ক্ষেত্রে পাইথন প্রক্রিয়া একই সাথে "অপেক্ষা করতে পারে" বা "উপলব্ধ ডেটা উপলব্ধ" এর জন্য অপেক্ষা করে, প্রক্রিয়াটির স্টিডিনে লিখে কেবল যখন রুম থাকে এবং তার স্টাডআউট এবং / অথবা স্ট্যাডার কেবল তখনই ডেটা প্রস্তুত থাকে reads proc.communicate()কোড (আসলে _communicateযেখানে লোমশ মামলা পরিচালনা করা হয়) আয় সব stdin ডেটা একবার (যদি থাকে) পাঠানো হয়েছে এবং সকল stdout- এ এবং / অথবা দ্বারা stderr ডেটা সঞ্চিত হয়েছে।
যদি আপনি উভয় stdoutএবং stderrদুটি ভিন্ন পাইপ (কোনও stdinপুনর্নির্দেশ নির্বিশেষে ) পড়তে চান তবে আপনারও অচলাবস্থা এড়াতে হবে। এখানে অচলাবস্থার পরিস্থিতিটি ভিন্ন — এটি ঘটে যখন আপনি stderrযখন উপাত্তগুলি থেকে ডেটা টানছেন stdout, বা বিপরীতে long তবে এটি এখনও রয়েছে long
ডেমো
আমি প্রতিশ্রুতি দিয়েছিলাম যে, পুনঃনির্দেশিত, পাইথন subprocessএস অন্তর্নিহিত স্টডআউটকে লিখেছেন, না sys.stdout। সুতরাং, এখানে কিছু কোড:
from cStringIO import StringIO
import os
import subprocess
import sys
def show1():
print 'start show1'
save = sys.stdout
sys.stdout = StringIO()
print 'sys.stdout being buffered'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
in_stdout = sys.stdout.getvalue()
sys.stdout = save
print 'in buffer:', in_stdout
def show2():
print 'start show2'
save = sys.stdout
sys.stdout = open(os.devnull, 'w')
print 'after redirect sys.stdout'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
sys.stdout = save
show1()
show2()
যখন রান করুন:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
নোট করুন যে প্রথম রুটিনটি যদি আপনি যুক্ত করেন তবে ব্যর্থ হবে stdout=sys.stdout, কারণ কোনও StringIOবস্তুর কোনও নেই fileno। দ্বিতীয় বর্জন করবে helloযদি আপনি যোগ stdout=sys.stdoutযেহেতু sys.stdoutথেকে আপনাকে পুনঃনির্দেশিত করা হয়েছে os.devnull।
(আপনি যদি পাইথনের ফাইল-বিবরণকারী -১ পুনর্নির্দেশ করেন তবে সাব-প্রসেস সেই পুনঃনির্দেশটি অনুসরণ করবে The open(os.devnull, 'w')কলটি এমন একটি প্রবাহ তৈরি করবে যার fileno()পরিমাণ ২ এর চেয়ে বেশি)
Popen.pollহিসাবে ব্যবহার করতে পারেন ।