Python - كيف يمكنني تمرير سلسلة إلى عملية فرعية.Popen (باستخدام الوسيطة stdin)؟

StackOverflow https://stackoverflow.com/questions/163542

  •  03-07-2019
  •  | 
  •  

سؤال

إذا قمت بما يلي:

import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]

انا حصلت:

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__
    (p2cread, p2cwrite,
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles
    p2cread = stdin.fileno()
AttributeError: 'cStringIO.StringI' object has no attribute 'fileno'

من الواضح أن كائن cStringIO.StringIO لا يقترب بدرجة كافية من ملف البط ليناسب العملية الفرعية.Popen.كيف يمكنني التغلب على هذا؟

هل كانت مفيدة؟

المحلول

Popen.communicate() توثيق:

لاحظ أنه إذا كنت ترغب في إرسال بيانات إلى stdin العملية ، فأنت بحاجة إلى إنشاء كائن popen باستخدام stdin = الأنابيب.وبالمثل ، للحصول على أي شيء آخر غير NONE في النتيجة ، تحتاج إلى إعطاء stdout = الأنابيب و/أو stderr = الأنابيب أيضًا.

استبدال os.popen*

    pipe = os.popen(cmd, 'w', bufsize)
    # ==>
    pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin

تحذير استخدم communication () بدلاً من stdin.write () أو stdout.read () أو stderr.read () لتجنب حالات الجمود بسبب أي من مخازن الأنابيب الأخرى التي تملأها عملية الطفل وحظرها.

لذلك يمكن كتابة المثال الخاص بك على النحو التالي:

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())
# -> four
# -> five
# ->

في الإصدار الحالي من Python 3، يمكنك استخدام subprocess.run, ، لتمرير الإدخال كسلسلة إلى أمر خارجي والحصول على حالة الخروج الخاصة به، وإخراجه كسلسلة مرة أخرى في مكالمة واحدة:

#!/usr/bin/env python3
from subprocess import run, PIPE

p = run(['grep', 'f'], stdout=PIPE,
        input='one\ntwo\nthree\nfour\nfive\nsix\n', encoding='ascii')
print(p.returncode)
# -> 0
print(p.stdout)
# -> four
# -> five
# -> 

نصائح أخرى

وأنا أحسب هذا الحل:

>>> p = subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
>>> p.stdin.write(b'one\ntwo\nthree\nfour\nfive\nsix\n') #expects a bytes type object
>>> p.communicate()[0]
'four\nfive\n'
>>> p.stdin.close()

هل هناك أحد أفضل؟

وأنا اقترح أحد بالدهشة بعض الشيء خلق الأنابيب، والذي هو في رأيي حتى أبسط طريقة لتمرير سلسلة لستدين من فرعي أو جانبي:

read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)

subprocess.check_call(['your-command'], stdin=read)

وهناك حل جميل إذا كنت تستخدم بايثون 3.4 أو أفضل. استخدام الوسيطة input بدلا من حجة stdin، التي تقبل حجة بايت:

output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)

وأستخدمه python3 وتبين أن تحتاج إلى ترميز سلسلة الخاص بك قبل أن تتمكن من تمريرها إلى ستدين:

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
out, err = p.communicate(input='one\ntwo\nthree\nfour\nfive\nsix\n'.encode())
print(out)

وأضاف "يبدو كائن cStringIO.StringIO لا الدجال قريبة بما فيه الكفاية لبطة ملف لتناسب subprocess.Popen"

: -)

وأخشى لا. الأنبوب هو مفهوم OS على مستوى منخفض، لذلك يتطلب على الاطلاق كائن الملف الذي يمثله واصف ملف على مستوى نظام التشغيل. الحل هو حق واحد.

from subprocess import Popen, PIPE
from tempfile import SpooledTemporaryFile as tempfile
f = tempfile()
f.write('one\ntwo\nthree\nfour\nfive\nsix\n')
f.seek(0)
print Popen(['/bin/grep','f'],stdout=PIPE,stdin=f).stdout.read()
f.close()
"""
Ex: Dialog (2-way) with a Popen()
"""

p = subprocess.Popen('Your Command Here',
                 stdout=subprocess.PIPE,
                 stderr=subprocess.STDOUT,
                 stdin=PIPE,
                 shell=True,
                 bufsize=0)
p.stdin.write('START\n')
out = p.stdout.readline()
while out:
  line = out
  line = line.rstrip("\n")

  if "WHATEVER1" in line:
      pr = 1
      p.stdin.write('DO 1\n')
      out = p.stdout.readline()
      continue

  if "WHATEVER2" in line:
      pr = 2
      p.stdin.write('DO 2\n')
      out = p.stdout.readline()
      continue
"""
..........
"""

out = p.stdout.readline()

p.wait()

وحذار أن Popen.communicate(input=s)may تعطي لك المتاعب ifsis كبير جدا، لأنه على ما يبدو عملية الأصل سوف العازلة أنه قبل التفرع في فرعي أو جانبي الطفل، وهذا يعني أنه يحتاج "مرتين قدر" الذاكرة المستخدمة في تلك المرحلة (على الأقل وفقا ل "تحت غطاء محرك السيارة" تفسير وثائق تم العثور عليها مرتبطة هنا ). في حالتي خاصة، swas مولد والتي توسعت لأول مرة بشكل كامل وعندها فقط مكتوبة tostdin ذلك كانت عملية الأصل حق ضخمة قبل ولدت الطفل، وكان لا يترك ذاكرة لمفترق فيه:

وFile "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py", line 1130, in _execute_child self.pid = os.fork() OSError: [Errno 12] Cannot allocate memory

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
p.stdin.write('one\n')
time.sleep(0.5)
p.stdin.write('two\n')
time.sleep(0.5)
p.stdin.write('three\n')
time.sleep(0.5)
testresult = p.communicate()[0]
time.sleep(0.5)
print(testresult)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top